| Index: content/common/gpu/media/omx_video_decode_accelerator.cc
|
| diff --git a/content/common/gpu/media/omx_video_decode_accelerator.cc b/content/common/gpu/media/omx_video_decode_accelerator.cc
|
| deleted file mode 100644
|
| index b110b7fbc3072fd17a7d81ea506c000bb235ef79..0000000000000000000000000000000000000000
|
| --- a/content/common/gpu/media/omx_video_decode_accelerator.cc
|
| +++ /dev/null
|
| @@ -1,1259 +0,0 @@
|
| -// Copyright (c) 2012 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/common/gpu/media/omx_video_decode_accelerator.h"
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/debug/trace_event.h"
|
| -#include "base/logging.h"
|
| -#include "base/stl_util.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "content/common/gpu/gpu_channel.h"
|
| -#include "content/common/gpu/media/gles2_texture_to_egl_image_translator.h"
|
| -#include "media/base/bitstream_buffer.h"
|
| -#include "media/video/picture.h"
|
| -
|
| -namespace content {
|
| -
|
| -// Helper typedef for input buffers. This is used as the pAppPrivate field of
|
| -// OMX_BUFFERHEADERTYPEs of input buffers, to point to the data associated with
|
| -// them.
|
| -typedef std::pair<scoped_ptr<base::SharedMemory>, int32> SharedMemoryAndId;
|
| -
|
| -enum { kNumPictureBuffers = 8 };
|
| -
|
| -// Delay between polling for texture sync status. 5ms feels like a good
|
| -// compromise, allowing some decoding ahead (up to 3 frames/vsync) to compensate
|
| -// for more difficult frames.
|
| -enum { kSyncPollDelayMs = 5 };
|
| -
|
| -void* omx_handle = NULL;
|
| -
|
| -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 = NULL;
|
| -OMXGetHandle omx_gethandle = NULL;
|
| -OMXGetComponentsOfRole omx_get_components_of_role = NULL;
|
| -OMXFreeHandle omx_free_handle = NULL;
|
| -OMXDeinit omx_deinit = NULL;
|
| -
|
| -// Maps h264-related Profile enum values to OMX_VIDEO_AVCPROFILETYPE values.
|
| -static OMX_U32 MapH264ProfileToOMXAVCProfile(uint32 profile) {
|
| - switch (profile) {
|
| - case media::H264PROFILE_BASELINE:
|
| - return OMX_VIDEO_AVCProfileBaseline;
|
| - case media::H264PROFILE_MAIN:
|
| - return OMX_VIDEO_AVCProfileMain;
|
| - case media::H264PROFILE_EXTENDED:
|
| - return OMX_VIDEO_AVCProfileExtended;
|
| - case media::H264PROFILE_HIGH:
|
| - return OMX_VIDEO_AVCProfileHigh;
|
| - case media::H264PROFILE_HIGH10PROFILE:
|
| - return OMX_VIDEO_AVCProfileHigh10;
|
| - case media::H264PROFILE_HIGH422PROFILE:
|
| - return OMX_VIDEO_AVCProfileHigh422;
|
| - case media::H264PROFILE_HIGH444PREDICTIVEPROFILE:
|
| - return OMX_VIDEO_AVCProfileHigh444;
|
| - // Below enums don't have equivalent enum in Openmax.
|
| - case media::H264PROFILE_SCALABLEBASELINE:
|
| - case media::H264PROFILE_SCALABLEHIGH:
|
| - case media::H264PROFILE_STEREOHIGH:
|
| - case media::H264PROFILE_MULTIVIEWHIGH:
|
| - // Nvidia OMX video decoder requires the same resources (as that of the
|
| - // High profile) in every profile higher to the Main profile.
|
| - return OMX_VIDEO_AVCProfileHigh444;
|
| - default:
|
| - NOTREACHED();
|
| - return OMX_VIDEO_AVCProfileMax;
|
| - }
|
| -}
|
| -
|
| -// Helper macros for dealing with failure. If |result| evaluates false, emit
|
| -// |log| to ERROR, register |error| with the decoder, and return |ret_val|
|
| -// (which may be omitted for functions that return void).
|
| -#define RETURN_ON_FAILURE(result, log, error, ret_val) \
|
| - do { \
|
| - if (!(result)) { \
|
| - DLOG(ERROR) << log; \
|
| - StopOnError(error); \
|
| - return ret_val; \
|
| - } \
|
| - } while (0)
|
| -
|
| -// OMX-specific version of RETURN_ON_FAILURE which compares with OMX_ErrorNone.
|
| -#define RETURN_ON_OMX_FAILURE(omx_result, log, error, ret_val) \
|
| - RETURN_ON_FAILURE( \
|
| - ((omx_result) == OMX_ErrorNone), \
|
| - log << ", OMX result: 0x" << std::hex << omx_result, \
|
| - error, ret_val)
|
| -
|
| -// static
|
| -bool OmxVideoDecodeAccelerator::pre_sandbox_init_done_ = false;
|
| -
|
| -class OmxVideoDecodeAccelerator::PictureSyncObject {
|
| - public:
|
| - // Create a sync object and insert into the GPU command stream.
|
| - PictureSyncObject(EGLDisplay egl_display);
|
| - ~PictureSyncObject();
|
| -
|
| - bool IsSynced();
|
| -
|
| - private:
|
| - EGLSyncKHR egl_sync_obj_;
|
| - EGLDisplay egl_display_;
|
| -};
|
| -
|
| -OmxVideoDecodeAccelerator::PictureSyncObject::PictureSyncObject(
|
| - EGLDisplay egl_display)
|
| - : egl_display_(egl_display) {
|
| - DCHECK(egl_display_ != EGL_NO_DISPLAY);
|
| -
|
| - egl_sync_obj_ = eglCreateSyncKHR(egl_display_, EGL_SYNC_FENCE_KHR, NULL);
|
| - DCHECK_NE(egl_sync_obj_, EGL_NO_SYNC_KHR);
|
| -}
|
| -
|
| -OmxVideoDecodeAccelerator::PictureSyncObject::~PictureSyncObject() {
|
| - eglDestroySyncKHR(egl_display_, egl_sync_obj_);
|
| -}
|
| -
|
| -bool OmxVideoDecodeAccelerator::PictureSyncObject::IsSynced() {
|
| - EGLint value = EGL_UNSIGNALED_KHR;
|
| - EGLBoolean ret = eglGetSyncAttribKHR(
|
| - egl_display_, egl_sync_obj_, EGL_SYNC_STATUS_KHR, &value);
|
| - DCHECK(ret) << "Failed getting sync object state.";
|
| -
|
| - return value == EGL_SIGNALED_KHR;
|
| -}
|
| -
|
| -OmxVideoDecodeAccelerator::OmxVideoDecodeAccelerator(
|
| - EGLDisplay egl_display, EGLContext egl_context,
|
| - media::VideoDecodeAccelerator::Client* client,
|
| - const base::Callback<bool(void)>& make_context_current)
|
| - : message_loop_(base::MessageLoop::current()),
|
| - component_handle_(NULL),
|
| - weak_this_(base::AsWeakPtr(this)),
|
| - init_begun_(false),
|
| - client_state_(OMX_StateMax),
|
| - current_state_change_(NO_TRANSITION),
|
| - input_buffer_count_(0),
|
| - input_buffer_size_(0),
|
| - input_port_(0),
|
| - input_buffers_at_component_(0),
|
| - output_port_(0),
|
| - output_buffers_at_component_(0),
|
| - egl_display_(egl_display),
|
| - egl_context_(egl_context),
|
| - make_context_current_(make_context_current),
|
| - client_ptr_factory_(client),
|
| - client_(client_ptr_factory_.GetWeakPtr()),
|
| - codec_(UNKNOWN),
|
| - h264_profile_(OMX_VIDEO_AVCProfileMax),
|
| - component_name_is_nvidia_(false) {
|
| - static bool omx_functions_initialized = PostSandboxInitialization();
|
| - RETURN_ON_FAILURE(omx_functions_initialized,
|
| - "Failed to load openmax library", PLATFORM_FAILURE,);
|
| - RETURN_ON_OMX_FAILURE(omx_init(), "Failed to init OpenMAX core",
|
| - PLATFORM_FAILURE,);
|
| -}
|
| -
|
| -OmxVideoDecodeAccelerator::~OmxVideoDecodeAccelerator() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - DCHECK(free_input_buffers_.empty());
|
| - DCHECK_EQ(0, input_buffers_at_component_);
|
| - DCHECK_EQ(0, output_buffers_at_component_);
|
| - DCHECK(pictures_.empty());
|
| -}
|
| -
|
| -// 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(media::VideoCodecProfile profile) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - if (profile >= media::H264PROFILE_MIN && profile <= media::H264PROFILE_MAX) {
|
| - codec_ = H264;
|
| - h264_profile_ = MapH264ProfileToOMXAVCProfile(profile);
|
| - RETURN_ON_FAILURE(h264_profile_ != OMX_VIDEO_AVCProfileMax,
|
| - "Unexpected profile", INVALID_ARGUMENT, false);
|
| - } else if (profile == media::VP8PROFILE_MAIN) {
|
| - codec_ = VP8;
|
| - } else {
|
| - RETURN_ON_FAILURE(false, "Unsupported profile: " << profile,
|
| - INVALID_ARGUMENT, false);
|
| - }
|
| -
|
| - // We need the context to be initialized to query extensions.
|
| - RETURN_ON_FAILURE(make_context_current_.Run(),
|
| - "Failed make context current",
|
| - PLATFORM_FAILURE,
|
| - false);
|
| - RETURN_ON_FAILURE(gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync,
|
| - "Platform does not support EGL_KHR_fence_sync",
|
| - PLATFORM_FAILURE,
|
| - false);
|
| -
|
| - if (!CreateComponent()) // Does its own RETURN_ON_FAILURE dances.
|
| - return false;
|
| -
|
| - DCHECK_EQ(current_state_change_, NO_TRANSITION);
|
| - current_state_change_ = INITIALIZING;
|
| - BeginTransitionToState(OMX_StateIdle);
|
| -
|
| - if (!AllocateInputBuffers()) // Does its own RETURN_ON_FAILURE dances.
|
| - return false;
|
| - if (!AllocateFakeOutputBuffers()) // Does its own RETURN_ON_FAILURE dances.
|
| - return false;
|
| -
|
| - init_begun_ = true;
|
| - return true;
|
| -}
|
| -
|
| -bool OmxVideoDecodeAccelerator::CreateComponent() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - OMX_CALLBACKTYPE omx_accelerator_callbacks = {
|
| - &OmxVideoDecodeAccelerator::EventHandler,
|
| - &OmxVideoDecodeAccelerator::EmptyBufferCallback,
|
| - &OmxVideoDecodeAccelerator::FillBufferCallback
|
| - };
|
| -
|
| - // TODO(vhiremath@nvidia.com) Get this role_name from the configs
|
| - // For now hard-coding.
|
| - OMX_STRING role_name = codec_ == H264 ?
|
| - const_cast<OMX_STRING>("video_decoder.avc") :
|
| - const_cast<OMX_STRING>("video_decoder.vpx");
|
| - // Get the first component for this role and set the role on it.
|
| - OMX_U32 num_components = 1;
|
| - std::string component(OMX_MAX_STRINGNAME_SIZE, '\0');
|
| - char* component_as_array = string_as_array(&component);
|
| - OMX_ERRORTYPE result = omx_get_components_of_role(
|
| - role_name, &num_components,
|
| - reinterpret_cast<OMX_U8**>(&component_as_array));
|
| - RETURN_ON_OMX_FAILURE(result, "Unsupported role: " << role_name,
|
| - PLATFORM_FAILURE, false);
|
| - RETURN_ON_FAILURE(num_components == 1, "No components for: " << role_name,
|
| - PLATFORM_FAILURE, false);
|
| - component_name_is_nvidia_ = StartsWithASCII(
|
| - component, "OMX.Nvidia", true);
|
| -
|
| - // Get the handle to the component.
|
| - result = omx_gethandle(
|
| - &component_handle_,
|
| - reinterpret_cast<OMX_STRING>(string_as_array(&component)),
|
| - this, &omx_accelerator_callbacks);
|
| - RETURN_ON_OMX_FAILURE(result,
|
| - "Failed to OMX_GetHandle on: " << component,
|
| - PLATFORM_FAILURE, false);
|
| - client_state_ = OMX_StateLoaded;
|
| -
|
| - texture_to_egl_image_translator_.reset(new Gles2TextureToEglImageTranslator(
|
| - StartsWithASCII(component, "OMX.SEC.", true)));
|
| -
|
| - // 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);
|
| - RETURN_ON_FAILURE(result == OMX_ErrorNone && port_param.nPorts == 2,
|
| - "Failed to get Port Param: " << result << ", "
|
| - << port_param.nPorts,
|
| - PLATFORM_FAILURE, false);
|
| - input_port_ = port_param.nStartPortNumber;
|
| - output_port_ = input_port_ + 1;
|
| -
|
| - // 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);
|
| - RETURN_ON_OMX_FAILURE(result, "Failed to Set Role",
|
| - PLATFORM_FAILURE, false);
|
| -
|
| - // 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);
|
| - RETURN_ON_OMX_FAILURE(result,
|
| - "GetParameter(OMX_IndexParamPortDefinition) failed",
|
| - PLATFORM_FAILURE, false);
|
| - RETURN_ON_FAILURE(OMX_DirInput == port_format.eDir, "Expected input port",
|
| - PLATFORM_FAILURE, false);
|
| -
|
| - input_buffer_count_ = port_format.nBufferCountActual;
|
| - input_buffer_size_ = port_format.nBufferSize;
|
| -
|
| - // Verify output port conforms to our expectations.
|
| - InitParam(*this, &port_format);
|
| - port_format.nPortIndex = output_port_;
|
| - result = OMX_GetParameter(component_handle_,
|
| - OMX_IndexParamPortDefinition,
|
| - &port_format);
|
| - RETURN_ON_OMX_FAILURE(result,
|
| - "GetParameter(OMX_IndexParamPortDefinition) failed",
|
| - PLATFORM_FAILURE, false);
|
| - RETURN_ON_FAILURE(OMX_DirOutput == port_format.eDir, "Expect Output Port",
|
| - PLATFORM_FAILURE, false);
|
| -
|
| - // Set output port parameters.
|
| - port_format.nBufferCountActual = kNumPictureBuffers;
|
| - // Force an OMX_EventPortSettingsChanged event to be sent once we know the
|
| - // stream's real dimensions (which can only happen once some Decode() work has
|
| - // been done).
|
| - port_format.format.video.nFrameWidth = -1;
|
| - port_format.format.video.nFrameHeight = -1;
|
| - result = OMX_SetParameter(component_handle_,
|
| - OMX_IndexParamPortDefinition,
|
| - &port_format);
|
| - RETURN_ON_OMX_FAILURE(result,
|
| - "SetParameter(OMX_IndexParamPortDefinition) failed",
|
| - PLATFORM_FAILURE, false);
|
| - return true;
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::Decode(
|
| - const media::BitstreamBuffer& bitstream_buffer) {
|
| - TRACE_EVENT1("Video Decoder", "OVDA::Decode",
|
| - "Buffer id", bitstream_buffer.id());
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - if (current_state_change_ == RESETTING ||
|
| - !queued_bitstream_buffers_.empty() ||
|
| - free_input_buffers_.empty()) {
|
| - queued_bitstream_buffers_.push_back(bitstream_buffer);
|
| - return;
|
| - }
|
| -
|
| - RETURN_ON_FAILURE((current_state_change_ == NO_TRANSITION ||
|
| - current_state_change_ == FLUSHING) &&
|
| - (client_state_ == OMX_StateIdle ||
|
| - client_state_ == OMX_StateExecuting),
|
| - "Call to Decode() during invalid state or transition: "
|
| - << current_state_change_ << ", " << client_state_,
|
| - ILLEGAL_STATE,);
|
| -
|
| - OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front();
|
| - free_input_buffers_.pop();
|
| -
|
| - if (bitstream_buffer.id() == -1 && bitstream_buffer.size() == 0) {
|
| - // Cook up an empty buffer w/ EOS set and feed it to OMX.
|
| - omx_buffer->nFilledLen = 0;
|
| - omx_buffer->nAllocLen = omx_buffer->nFilledLen;
|
| - omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
|
| - omx_buffer->nTimeStamp = -2;
|
| - OMX_ERRORTYPE result = OMX_EmptyThisBuffer(component_handle_, omx_buffer);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_EmptyThisBuffer() failed",
|
| - PLATFORM_FAILURE,);
|
| - input_buffers_at_component_++;
|
| - return;
|
| - }
|
| -
|
| - // Setup |omx_buffer|.
|
| - scoped_ptr<base::SharedMemory> shm(
|
| - new base::SharedMemory(bitstream_buffer.handle(), true));
|
| - RETURN_ON_FAILURE(shm->Map(bitstream_buffer.size()),
|
| - "Failed to SharedMemory::Map()", UNREADABLE_INPUT,);
|
| -
|
| - SharedMemoryAndId* input_buffer_details = new SharedMemoryAndId();
|
| - input_buffer_details->first.reset(shm.release());
|
| - input_buffer_details->second = bitstream_buffer.id();
|
| - DCHECK(!omx_buffer->pAppPrivate);
|
| - omx_buffer->pAppPrivate = input_buffer_details;
|
| - omx_buffer->pBuffer =
|
| - static_cast<OMX_U8*>(input_buffer_details->first->memory());
|
| - omx_buffer->nFilledLen = bitstream_buffer.size();
|
| - omx_buffer->nAllocLen = omx_buffer->nFilledLen;
|
| - omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
|
| - // Abuse the header's nTimeStamp field to propagate the bitstream buffer ID to
|
| - // the output buffer's nTimeStamp field, so we can report it back to the
|
| - // client in PictureReady().
|
| - omx_buffer->nTimeStamp = bitstream_buffer.id();
|
| -
|
| - // Give this buffer to OMX.
|
| - OMX_ERRORTYPE result = OMX_EmptyThisBuffer(component_handle_, omx_buffer);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_EmptyThisBuffer() failed",
|
| - PLATFORM_FAILURE,);
|
| - input_buffers_at_component_++;
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::AssignPictureBuffers(
|
| - const std::vector<media::PictureBuffer>& buffers) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - // If we are resetting/destroying/erroring, don't bother, as
|
| - // OMX_FillThisBuffer will fail anyway. In case we're in the middle of
|
| - // closing, this will put the Accelerator in ERRORING mode, which has the
|
| - // unwanted side effect of not going through the OMX_FreeBuffers path and
|
| - // leaks memory.
|
| - if (current_state_change_ == RESETTING ||
|
| - current_state_change_ == DESTROYING ||
|
| - current_state_change_ == ERRORING)
|
| - return;
|
| -
|
| - RETURN_ON_FAILURE(CanFillBuffer(), "Can't fill buffer", ILLEGAL_STATE,);
|
| - RETURN_ON_FAILURE((kNumPictureBuffers == buffers.size()),
|
| - "Failed to provide requested picture buffers. (Got " << buffers.size() <<
|
| - ", requested " << kNumPictureBuffers << ")", INVALID_ARGUMENT,);
|
| -
|
| - DCHECK_EQ(output_buffers_at_component_, 0);
|
| - DCHECK_EQ(fake_output_buffers_.size(), 0U);
|
| - DCHECK_EQ(pictures_.size(), 0U);
|
| -
|
| - if (!make_context_current_.Run())
|
| - return;
|
| -
|
| - for (size_t i = 0; i < buffers.size(); ++i) {
|
| - EGLImageKHR egl_image =
|
| - texture_to_egl_image_translator_->TranslateToEglImage(
|
| - egl_display_, egl_context_,
|
| - buffers[i].texture_id(),
|
| - last_requested_picture_buffer_dimensions_);
|
| - CHECK(pictures_.insert(std::make_pair(
|
| - buffers[i].id(), OutputPicture(buffers[i], NULL, egl_image))).second);
|
| - }
|
| -
|
| - // These do their own RETURN_ON_FAILURE dances.
|
| - if (!AllocateOutputBuffers())
|
| - return;
|
| - if (!SendCommandToPort(OMX_CommandPortEnable, output_port_))
|
| - return;
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - TRACE_EVENT1("Video Decoder", "OVDA::ReusePictureBuffer",
|
| - "Picture id", picture_buffer_id);
|
| - scoped_ptr<PictureSyncObject> egl_sync_obj(
|
| - new PictureSyncObject(egl_display_));
|
| -
|
| - // Start checking sync status periodically.
|
| - CheckPictureStatus(picture_buffer_id, egl_sync_obj.Pass());
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::CheckPictureStatus(
|
| - int32 picture_buffer_id,
|
| - scoped_ptr<PictureSyncObject> egl_sync_obj) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - // It's possible for this task to never run if the message loop is
|
| - // stopped. In that case we may never call QueuePictureBuffer().
|
| - // This is fine though, because all pictures, irrespective of their state,
|
| - // are in pictures_ map and that's what will be used to do the clean up.
|
| - if (!egl_sync_obj->IsSynced()) {
|
| - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind(
|
| - &OmxVideoDecodeAccelerator::CheckPictureStatus, weak_this_,
|
| - picture_buffer_id, base::Passed(&egl_sync_obj)),
|
| - base::TimeDelta::FromMilliseconds(kSyncPollDelayMs));
|
| - return;
|
| - }
|
| -
|
| - // Synced successfully. Queue the buffer for reuse.
|
| - QueuePictureBuffer(picture_buffer_id);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::QueuePictureBuffer(int32 picture_buffer_id) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - // During port-flushing, do not call OMX FillThisBuffer.
|
| - if (current_state_change_ == RESETTING) {
|
| - queued_picture_buffer_ids_.push_back(picture_buffer_id);
|
| - return;
|
| - }
|
| -
|
| - // We might have started destroying while waiting for the picture. It's safe
|
| - // to drop it here, because we will free all the pictures regardless of their
|
| - // state using the pictures_ map.
|
| - if (!CanFillBuffer())
|
| - return;
|
| -
|
| - OutputPictureById::iterator it = pictures_.find(picture_buffer_id);
|
| - RETURN_ON_FAILURE(it != pictures_.end(),
|
| - "Missing picture buffer id: " << picture_buffer_id,
|
| - INVALID_ARGUMENT,);
|
| - OutputPicture& output_picture = it->second;
|
| -
|
| - ++output_buffers_at_component_;
|
| - OMX_ERRORTYPE result =
|
| - OMX_FillThisBuffer(component_handle_, output_picture.omx_buffer_header);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_FillThisBuffer() failed",
|
| - PLATFORM_FAILURE,);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::Flush() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - DCHECK_EQ(current_state_change_, NO_TRANSITION);
|
| - DCHECK_EQ(client_state_, OMX_StateExecuting);
|
| - current_state_change_ = FLUSHING;
|
| -
|
| - Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0));
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnReachedEOSInFlushing() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - DCHECK_EQ(client_state_, OMX_StateExecuting);
|
| - current_state_change_ = NO_TRANSITION;
|
| - if (client_)
|
| - client_->NotifyFlushDone();
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::FlushIOPorts() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - // Flush input port first.
|
| - if (!SendCommandToPort(OMX_CommandFlush, input_port_))
|
| - return;
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::InputPortFlushDone() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - DCHECK_EQ(input_buffers_at_component_, 0);
|
| - if (!SendCommandToPort(OMX_CommandFlush, output_port_))
|
| - return;
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OutputPortFlushDone() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - DCHECK_EQ(output_buffers_at_component_, 0);
|
| - BeginTransitionToState(OMX_StateExecuting);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::Reset() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - DCHECK_EQ(current_state_change_, NO_TRANSITION);
|
| - DCHECK_EQ(client_state_, OMX_StateExecuting);
|
| - current_state_change_ = RESETTING;
|
| - BeginTransitionToState(OMX_StatePause);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::Destroy() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - scoped_ptr<OmxVideoDecodeAccelerator> deleter(this);
|
| - client_ptr_factory_.InvalidateWeakPtrs();
|
| -
|
| - if (current_state_change_ == ERRORING ||
|
| - current_state_change_ == DESTROYING) {
|
| - return;
|
| - }
|
| -
|
| - DCHECK(current_state_change_ == NO_TRANSITION ||
|
| - current_state_change_ == FLUSHING ||
|
| - current_state_change_ == RESETTING) << current_state_change_;
|
| -
|
| - // If we were never initializeed there's no teardown to do.
|
| - if (client_state_ == OMX_StateMax)
|
| - return;
|
| - // If we can already call OMX_FreeHandle, simply do so.
|
| - if (client_state_ == OMX_StateInvalid || client_state_ == OMX_StateLoaded) {
|
| - ShutdownComponent();
|
| - return;
|
| - }
|
| - DCHECK(client_state_ == OMX_StateExecuting ||
|
| - client_state_ == OMX_StateIdle ||
|
| - client_state_ == OMX_StatePause);
|
| - current_state_change_ = DESTROYING;
|
| - BeginTransitionToState(OMX_StateIdle);
|
| - BusyLoopInDestroying(deleter.Pass());
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::BeginTransitionToState(
|
| - OMX_STATETYPE new_state) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - if (new_state != OMX_StateInvalid)
|
| - DCHECK_NE(current_state_change_, NO_TRANSITION);
|
| - if (current_state_change_ == ERRORING)
|
| - return;
|
| - OMX_ERRORTYPE result = OMX_SendCommand(
|
| - component_handle_, OMX_CommandStateSet, new_state, 0);
|
| - RETURN_ON_OMX_FAILURE(result, "SendCommand(OMX_CommandStateSet) failed",
|
| - PLATFORM_FAILURE,);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnReachedIdleInInitializing() {
|
| - DCHECK_EQ(client_state_, OMX_StateLoaded);
|
| - client_state_ = OMX_StateIdle;
|
| - // Query the resources with the component.
|
| - if (component_name_is_nvidia_) {
|
| - OMX_INDEXTYPE extension_index;
|
| - OMX_ERRORTYPE result = OMX_GetExtensionIndex(
|
| - component_handle_,
|
| - const_cast<char*>("OMX.Nvidia.index.config.checkresources"),
|
| - &extension_index);
|
| - RETURN_ON_OMX_FAILURE(result,
|
| - "Failed to get the extension",
|
| - PLATFORM_FAILURE,);
|
| - OMX_VIDEO_PARAM_PROFILELEVELTYPE video_profile_level;
|
| - InitParam(*this, &video_profile_level);
|
| - DCHECK_EQ(codec_, H264);
|
| - video_profile_level.eProfile = h264_profile_;
|
| - result = OMX_SetConfig(component_handle_, extension_index,
|
| - &video_profile_level);
|
| - RETURN_ON_OMX_FAILURE(result,
|
| - "Resource Allocation failed",
|
| - PLATFORM_FAILURE,);
|
| -
|
| - // The OMX spec doesn't say whether (0,0) is bottom-left or top-left, but
|
| - // NVIDIA OMX implementation used with this class chooses the opposite
|
| - // of other APIs used for HW decode (DXVA, OS/X, VAAPI). So we request
|
| - // a mirror here to avoid having to track Y-orientation throughout the
|
| - // stack (particularly unattractive because this is exposed to ppapi
|
| - // plugin authors and NaCl programs).
|
| - OMX_CONFIG_MIRRORTYPE mirror_config;
|
| - InitParam(*this, &mirror_config);
|
| - result = OMX_GetConfig(component_handle_,
|
| - OMX_IndexConfigCommonMirror, &mirror_config);
|
| - RETURN_ON_OMX_FAILURE(result, "Failed to get mirror", PLATFORM_FAILURE,);
|
| - mirror_config.eMirror = OMX_MirrorVertical;
|
| - result = OMX_SetConfig(component_handle_,
|
| - OMX_IndexConfigCommonMirror, &mirror_config);
|
| - RETURN_ON_OMX_FAILURE(result, "Failed to set mirror", PLATFORM_FAILURE,);
|
| - }
|
| - BeginTransitionToState(OMX_StateExecuting);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnReachedExecutingInInitializing() {
|
| - DCHECK_EQ(client_state_, OMX_StateIdle);
|
| - client_state_ = OMX_StateExecuting;
|
| - current_state_change_ = NO_TRANSITION;
|
| -
|
| - // Request filling of our fake buffers to trigger decode processing. In
|
| - // reality as soon as any data is decoded these will get dismissed due to
|
| - // dimension mismatch.
|
| - for (std::set<OMX_BUFFERHEADERTYPE*>::iterator it =
|
| - fake_output_buffers_.begin();
|
| - it != fake_output_buffers_.end(); ++it) {
|
| - OMX_BUFFERHEADERTYPE* buffer = *it;
|
| - OMX_ERRORTYPE result = OMX_FillThisBuffer(component_handle_, buffer);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_FillThisBuffer()", PLATFORM_FAILURE,);
|
| - ++output_buffers_at_component_;
|
| - }
|
| -
|
| - if (client_)
|
| - client_->NotifyInitializeDone();
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnReachedPauseInResetting() {
|
| - DCHECK_EQ(client_state_, OMX_StateExecuting);
|
| - client_state_ = OMX_StatePause;
|
| - FlushIOPorts();
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::DecodeQueuedBitstreamBuffers() {
|
| - BitstreamBufferList buffers;
|
| - buffers.swap(queued_bitstream_buffers_);
|
| - if (current_state_change_ == DESTROYING ||
|
| - current_state_change_ == ERRORING) {
|
| - return;
|
| - }
|
| - for (size_t i = 0; i < buffers.size(); ++i)
|
| - Decode(buffers[i]);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnReachedExecutingInResetting() {
|
| - DCHECK_EQ(client_state_, OMX_StatePause);
|
| - client_state_ = OMX_StateExecuting;
|
| - current_state_change_ = NO_TRANSITION;
|
| - if (!client_)
|
| - return;
|
| -
|
| - // Drain queues of input & output buffers held during the reset.
|
| - DecodeQueuedBitstreamBuffers();
|
| - for (size_t i = 0; i < queued_picture_buffer_ids_.size(); ++i)
|
| - ReusePictureBuffer(queued_picture_buffer_ids_[i]);
|
| - queued_picture_buffer_ids_.clear();
|
| -
|
| - client_->NotifyResetDone();
|
| -}
|
| -
|
| -// Alert: HORROR ahead! OMX shutdown is an asynchronous dance but our clients
|
| -// enjoy the fire-and-forget nature of a synchronous Destroy() call that
|
| -// ensures no further callbacks are made. Since the interface between OMX
|
| -// callbacks and this class is a MessageLoop, we need to ensure the loop
|
| -// outlives the shutdown dance, even during process shutdown. We do this by
|
| -// repeatedly enqueuing a no-op task until shutdown is complete, since
|
| -// MessageLoop's shutdown drains pending tasks.
|
| -void OmxVideoDecodeAccelerator::BusyLoopInDestroying(
|
| - scoped_ptr<OmxVideoDecodeAccelerator> self) {
|
| - if (!component_handle_) return;
|
| - // Can't use PostDelayedTask here because MessageLoop doesn't drain delayed
|
| - // tasks. Instead we sleep for 5ms. Really.
|
| - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5));
|
| - base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
|
| - &OmxVideoDecodeAccelerator::BusyLoopInDestroying,
|
| - base::Unretained(this), base::Passed(&self)));
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnReachedIdleInDestroying() {
|
| - DCHECK(client_state_ == OMX_StateExecuting ||
|
| - client_state_ == OMX_StateIdle ||
|
| - client_state_ == OMX_StatePause);
|
| - client_state_ = OMX_StateIdle;
|
| -
|
| - // Note that during the Executing -> Idle transition, the OMX spec guarantees
|
| - // buffers have been returned to the client, so we don't need to do an
|
| - // explicit FlushIOPorts().
|
| -
|
| - BeginTransitionToState(OMX_StateLoaded);
|
| -
|
| - FreeOMXBuffers();
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnReachedLoadedInDestroying() {
|
| - DCHECK_EQ(client_state_, OMX_StateIdle);
|
| - client_state_ = OMX_StateLoaded;
|
| - current_state_change_ = NO_TRANSITION;
|
| - ShutdownComponent();
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnReachedInvalidInErroring() {
|
| - client_state_ = OMX_StateInvalid;
|
| - FreeOMXBuffers();
|
| - ShutdownComponent();
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::ShutdownComponent() {
|
| - OMX_ERRORTYPE result = omx_free_handle(component_handle_);
|
| - if (result != OMX_ErrorNone)
|
| - DLOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result;
|
| - client_state_ = OMX_StateMax;
|
| - omx_deinit();
|
| - // Allow BusyLoopInDestroying to exit and delete |this|.
|
| - component_handle_ = NULL;
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::StopOnError(
|
| - media::VideoDecodeAccelerator::Error error) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - if (current_state_change_ == ERRORING)
|
| - return;
|
| -
|
| - if (client_ && init_begun_)
|
| - client_->NotifyError(error);
|
| - client_ptr_factory_.InvalidateWeakPtrs();
|
| -
|
| - if (client_state_ == OMX_StateInvalid || client_state_ == OMX_StateMax)
|
| - return;
|
| -
|
| - BeginTransitionToState(OMX_StateInvalid);
|
| - current_state_change_ = ERRORING;
|
| -}
|
| -
|
| -bool OmxVideoDecodeAccelerator::AllocateInputBuffers() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - for (int i = 0; i < input_buffer_count_; ++i) {
|
| - OMX_BUFFERHEADERTYPE* buffer;
|
| - // While registering the buffer header we use fake buffer information
|
| - // (length 0, at memory address 0x1) to fake out the "safety" check in
|
| - // OMX_UseBuffer. When it comes time to actually use this header in Decode
|
| - // we set these fields to their real values (for the duration of that
|
| - // Decode).
|
| - OMX_ERRORTYPE result =
|
| - OMX_UseBuffer(component_handle_, &buffer, input_port_,
|
| - NULL, /* pAppPrivate gets set in Decode(). */
|
| - 0, reinterpret_cast<OMX_U8*>(0x1));
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_UseBuffer() Input buffer error",
|
| - PLATFORM_FAILURE, false);
|
| - buffer->nInputPortIndex = input_port_;
|
| - buffer->nOffset = 0;
|
| - buffer->nFlags = 0;
|
| - free_input_buffers_.push(buffer);
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool OmxVideoDecodeAccelerator::AllocateFakeOutputBuffers() {
|
| - // Fill the component with fake output buffers.
|
| - for (unsigned int i = 0; i < kNumPictureBuffers; ++i) {
|
| - OMX_BUFFERHEADERTYPE* buffer;
|
| - OMX_ERRORTYPE result;
|
| - result = OMX_AllocateBuffer(component_handle_, &buffer, output_port_,
|
| - NULL, 0);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_AllocateBuffer failed",
|
| - PLATFORM_FAILURE, false);
|
| - buffer->pAppPrivate = NULL;
|
| - buffer->nTimeStamp = -1;
|
| - buffer->nOutputPortIndex = output_port_;
|
| - CHECK(fake_output_buffers_.insert(buffer).second);
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool OmxVideoDecodeAccelerator::AllocateOutputBuffers() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - DCHECK(!pictures_.empty());
|
| - for (OutputPictureById::iterator it = pictures_.begin();
|
| - it != pictures_.end(); ++it) {
|
| - media::PictureBuffer& picture_buffer = it->second.picture_buffer;
|
| - OMX_BUFFERHEADERTYPE** omx_buffer = &it->second.omx_buffer_header;
|
| - DCHECK(!*omx_buffer);
|
| - OMX_ERRORTYPE result = OMX_UseEGLImage(
|
| - component_handle_, omx_buffer, output_port_, &picture_buffer,
|
| - it->second.egl_image);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_UseEGLImage", PLATFORM_FAILURE, false);
|
| - // Here we set a garbage bitstream buffer id, and then overwrite it before
|
| - // passing to PictureReady.
|
| - int garbage_bitstream_buffer_id = -1;
|
| - (*omx_buffer)->pAppPrivate =
|
| - new media::Picture(picture_buffer.id(), garbage_bitstream_buffer_id);
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::FreeOMXBuffers() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - bool failure_seen = false;
|
| - while (!free_input_buffers_.empty()) {
|
| - OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front();
|
| - free_input_buffers_.pop();
|
| - OMX_ERRORTYPE result =
|
| - OMX_FreeBuffer(component_handle_, input_port_, omx_buffer);
|
| - if (result != OMX_ErrorNone) {
|
| - DLOG(ERROR) << "OMX_FreeBuffer failed: 0x" << std::hex << result;
|
| - failure_seen = true;
|
| - }
|
| - }
|
| - for (OutputPictureById::iterator it = pictures_.begin();
|
| - it != pictures_.end(); ++it) {
|
| - OMX_BUFFERHEADERTYPE* omx_buffer = it->second.omx_buffer_header;
|
| - DCHECK(omx_buffer);
|
| - delete reinterpret_cast<media::Picture*>(omx_buffer->pAppPrivate);
|
| - OMX_ERRORTYPE result =
|
| - OMX_FreeBuffer(component_handle_, output_port_, omx_buffer);
|
| - if (result != OMX_ErrorNone) {
|
| - DLOG(ERROR) << "OMX_FreeBuffer failed: 0x" << std::hex << result;
|
| - failure_seen = true;
|
| - }
|
| - texture_to_egl_image_translator_->DestroyEglImage(egl_display_,
|
| - it->second.egl_image);
|
| - if (client_)
|
| - client_->DismissPictureBuffer(it->first);
|
| - }
|
| - pictures_.clear();
|
| -
|
| - // Delete pending fake_output_buffers_
|
| - for (std::set<OMX_BUFFERHEADERTYPE*>::iterator it =
|
| - fake_output_buffers_.begin();
|
| - it != fake_output_buffers_.end(); ++it) {
|
| - OMX_BUFFERHEADERTYPE* buffer = *it;
|
| - OMX_ERRORTYPE result =
|
| - OMX_FreeBuffer(component_handle_, output_port_, buffer);
|
| - if (result != OMX_ErrorNone) {
|
| - DLOG(ERROR) << "OMX_FreeBuffer failed: 0x" << std::hex << result;
|
| - failure_seen = true;
|
| - }
|
| - }
|
| - fake_output_buffers_.clear();
|
| -
|
| - // Dequeue pending queued_picture_buffer_ids_
|
| - if (client_) {
|
| - for (size_t i = 0; i < queued_picture_buffer_ids_.size(); ++i)
|
| - client_->DismissPictureBuffer(queued_picture_buffer_ids_[i]);
|
| - }
|
| - queued_picture_buffer_ids_.clear();
|
| -
|
| - RETURN_ON_FAILURE(!failure_seen, "OMX_FreeBuffer", PLATFORM_FAILURE,);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnOutputPortDisabled() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - 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);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_GetParameter", PLATFORM_FAILURE,);
|
| - DCHECK_LE(port_format.nBufferCountMin, kNumPictureBuffers);
|
| -
|
| - // TODO(fischman): to support mid-stream resize, need to free/dismiss any
|
| - // |pictures_| we already have. Make sure that the shutdown-path agrees with
|
| - // this (there's already freeing logic there, which should not be duplicated).
|
| -
|
| - // Request picture buffers to be handed to the component.
|
| - // ProvidePictureBuffers() will trigger AssignPictureBuffers, which ultimately
|
| - // assigns the textures to the component and re-enables the port.
|
| - const OMX_VIDEO_PORTDEFINITIONTYPE& vformat = port_format.format.video;
|
| - last_requested_picture_buffer_dimensions_.SetSize(vformat.nFrameWidth,
|
| - vformat.nFrameHeight);
|
| - if (client_) {
|
| - client_->ProvidePictureBuffers(
|
| - kNumPictureBuffers,
|
| - gfx::Size(vformat.nFrameWidth, vformat.nFrameHeight),
|
| - GL_TEXTURE_2D);
|
| - }
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::OnOutputPortEnabled() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| -
|
| - if (current_state_change_ == RESETTING) {
|
| - for (OutputPictureById::iterator it = pictures_.begin();
|
| - it != pictures_.end(); ++it) {
|
| - queued_picture_buffer_ids_.push_back(it->first);
|
| - }
|
| - return;
|
| - }
|
| -
|
| - if (!CanFillBuffer()) {
|
| - StopOnError(ILLEGAL_STATE);
|
| - return;
|
| - }
|
| -
|
| - // Provide output buffers to decoder.
|
| - for (OutputPictureById::iterator it = pictures_.begin();
|
| - it != pictures_.end(); ++it) {
|
| - OMX_BUFFERHEADERTYPE* omx_buffer = it->second.omx_buffer_header;
|
| - DCHECK(omx_buffer);
|
| - // 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);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_FillThisBuffer() failed",
|
| - PLATFORM_FAILURE,);
|
| - }
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::FillBufferDoneTask(
|
| - OMX_BUFFERHEADERTYPE* buffer) {
|
| -
|
| - media::Picture* picture =
|
| - reinterpret_cast<media::Picture*>(buffer->pAppPrivate);
|
| - int picture_buffer_id = picture ? picture->picture_buffer_id() : -1;
|
| - TRACE_EVENT2("Video Decoder", "OVDA::FillBufferDoneTask",
|
| - "Buffer id", buffer->nTimeStamp,
|
| - "Picture id", picture_buffer_id);
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - DCHECK_GT(output_buffers_at_component_, 0);
|
| - --output_buffers_at_component_;
|
| -
|
| - // If we are destroying and then get a fillbuffer callback, calling into any
|
| - // openmax function will put us in error mode, so bail now. In the RESETTING
|
| - // case we still need to enqueue the picture ids but have to avoid giving
|
| - // them to the client (this is handled below).
|
| - if (current_state_change_ == DESTROYING ||
|
| - current_state_change_ == ERRORING)
|
| - return;
|
| -
|
| - if (fake_output_buffers_.size() && fake_output_buffers_.count(buffer)) {
|
| - size_t erased = fake_output_buffers_.erase(buffer);
|
| - DCHECK_EQ(erased, 1U);
|
| - OMX_ERRORTYPE result =
|
| - OMX_FreeBuffer(component_handle_, output_port_, buffer);
|
| - RETURN_ON_OMX_FAILURE(result, "OMX_FreeBuffer failed", PLATFORM_FAILURE,);
|
| - return;
|
| - }
|
| - DCHECK(!fake_output_buffers_.size());
|
| -
|
| - // When the EOS picture is delivered back to us, notify the client and reuse
|
| - // the underlying picturebuffer.
|
| - if (buffer->nFlags & OMX_BUFFERFLAG_EOS) {
|
| - buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
|
| - OnReachedEOSInFlushing();
|
| - ReusePictureBuffer(picture_buffer_id);
|
| - return;
|
| - }
|
| -
|
| - // During the transition from Executing to Idle, and during port-flushing, all
|
| - // pictures are sent back through here. Avoid giving them to the client.
|
| - if (current_state_change_ == RESETTING) {
|
| - queued_picture_buffer_ids_.push_back(picture_buffer_id);
|
| - return;
|
| - }
|
| -
|
| - DCHECK(picture);
|
| - // See Decode() for an explanation of this abuse of nTimeStamp.
|
| - picture->set_bitstream_buffer_id(buffer->nTimeStamp);
|
| - if (client_)
|
| - client_->PictureReady(*picture);
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::EmptyBufferDoneTask(
|
| - OMX_BUFFERHEADERTYPE* buffer) {
|
| - TRACE_EVENT1("Video Decoder", "OVDA::EmptyBufferDoneTask",
|
| - "Buffer id", buffer->nTimeStamp);
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - 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 BitstreamBuffer's id and notify the client of
|
| - // its completion.
|
| - SharedMemoryAndId* input_buffer_details =
|
| - reinterpret_cast<SharedMemoryAndId*>(buffer->pAppPrivate);
|
| - DCHECK(input_buffer_details);
|
| - buffer->pAppPrivate = NULL;
|
| - if (client_)
|
| - client_->NotifyEndOfBitstreamBuffer(input_buffer_details->second);
|
| - delete input_buffer_details;
|
| -
|
| - DecodeQueuedBitstreamBuffers();
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::DispatchStateReached(OMX_STATETYPE reached) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - switch (current_state_change_) {
|
| - case INITIALIZING:
|
| - switch (reached) {
|
| - case OMX_StateIdle:
|
| - OnReachedIdleInInitializing();
|
| - return;
|
| - case OMX_StateExecuting:
|
| - OnReachedExecutingInInitializing();
|
| - return;
|
| - default:
|
| - NOTREACHED() << "Unexpected state in INITIALIZING: " << reached;
|
| - return;
|
| - }
|
| - case RESETTING:
|
| - switch (reached) {
|
| - case OMX_StatePause:
|
| - OnReachedPauseInResetting();
|
| - return;
|
| - case OMX_StateExecuting:
|
| - OnReachedExecutingInResetting();
|
| - return;
|
| - default:
|
| - NOTREACHED() << "Unexpected state in RESETTING: " << reached;
|
| - return;
|
| - }
|
| - case DESTROYING:
|
| - switch (reached) {
|
| - case OMX_StatePause:
|
| - case OMX_StateExecuting:
|
| - // Because Destroy() can interrupt an in-progress Reset(),
|
| - // we might arrive at these states after current_state_change_ was
|
| - // overwritten with DESTROYING. That's fine though - we already have
|
| - // the state transition for Destroy() queued up at the component, so
|
| - // we treat this as a no-op.
|
| - return;
|
| - case OMX_StateIdle:
|
| - OnReachedIdleInDestroying();
|
| - return;
|
| - case OMX_StateLoaded:
|
| - OnReachedLoadedInDestroying();
|
| - return;
|
| - default:
|
| - NOTREACHED() << "Unexpected state in DESTROYING: " << reached;
|
| - return;
|
| - }
|
| - case ERRORING:
|
| - switch (reached) {
|
| - case OMX_StateInvalid:
|
| - OnReachedInvalidInErroring();
|
| - return;
|
| - default:
|
| - NOTREACHED() << "Unexpected state in ERRORING: " << reached;
|
| - return;
|
| - }
|
| - default:
|
| - NOTREACHED() << "Unexpected state in " << current_state_change_
|
| - << ": " << reached;
|
| - }
|
| -}
|
| -
|
| -void OmxVideoDecodeAccelerator::EventHandlerCompleteTask(OMX_EVENTTYPE event,
|
| - OMX_U32 data1,
|
| - OMX_U32 data2) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - switch (event) {
|
| - case OMX_EventCmdComplete:
|
| - switch (data1) {
|
| - case OMX_CommandPortDisable:
|
| - DCHECK_EQ(data2, output_port_);
|
| - OnOutputPortDisabled();
|
| - return;
|
| - case OMX_CommandPortEnable:
|
| - DCHECK_EQ(data2, output_port_);
|
| - OnOutputPortEnabled();
|
| - return;
|
| - case OMX_CommandStateSet:
|
| - DispatchStateReached(static_cast<OMX_STATETYPE>(data2));
|
| - return;
|
| - case OMX_CommandFlush:
|
| - if (current_state_change_ == DESTROYING ||
|
| - current_state_change_ == ERRORING) {
|
| - return;
|
| - }
|
| - DCHECK_EQ(current_state_change_, RESETTING);
|
| - if (data2 == input_port_)
|
| - InputPortFlushDone();
|
| - else if (data2 == output_port_)
|
| - OutputPortFlushDone();
|
| - else
|
| - NOTREACHED() << "Unexpected port flushed: " << data2;
|
| - return;
|
| - default:
|
| - RETURN_ON_FAILURE(false, "Unknown command completed: " << data1,
|
| - PLATFORM_FAILURE,);
|
| - }
|
| - return;
|
| - case OMX_EventError:
|
| - if (current_state_change_ != DESTROYING &&
|
| - current_state_change_ != ERRORING) {
|
| - RETURN_ON_FAILURE(false, "EventError: 0x" << std::hex << data1,
|
| - PLATFORM_FAILURE,);
|
| - }
|
| - return;
|
| - case OMX_EventPortSettingsChanged:
|
| - if ((data2 == OMX_IndexParamPortDefinition) || // Tegra2/3
|
| - (data2 == 0)) { // Exynos SEC-OMX; http://crosbug.com/p/11665
|
| - DCHECK_EQ(data1, output_port_);
|
| - // This event is only used for output resize; kick off handling that by
|
| - // pausing the output port.
|
| - SendCommandToPort(OMX_CommandPortDisable, output_port_);
|
| - } else if (data1 == output_port_ &&
|
| - data2 == OMX_IndexConfigCommonOutputCrop) {
|
| - // TODO(vjain): Handle video crop rect.
|
| - } else if (data1 == output_port_ &&
|
| - data2 == OMX_IndexConfigCommonScale) {
|
| - // TODO(ashokm@nvidia.com): Handle video SAR change.
|
| - } else {
|
| - RETURN_ON_FAILURE(false,
|
| - "Unexpected EventPortSettingsChanged: "
|
| - << data1 << ", " << data2,
|
| - PLATFORM_FAILURE,);
|
| - }
|
| - return;
|
| - case OMX_EventBufferFlag:
|
| - if (data1 == output_port_) {
|
| - // In case of Destroy() interrupting Flush().
|
| - if (current_state_change_ == DESTROYING)
|
| - return;
|
| - DCHECK_EQ(current_state_change_, FLUSHING);
|
| - // Do nothing; rely on the EOS picture delivery to notify the client.
|
| - } else {
|
| - RETURN_ON_FAILURE(false,
|
| - "Unexpected OMX_EventBufferFlag: "
|
| - << data1 << ", " << data2,
|
| - PLATFORM_FAILURE,);
|
| - }
|
| - return;
|
| - default:
|
| - RETURN_ON_FAILURE(false, "Unexpected unhandled event: " << event,
|
| - PLATFORM_FAILURE,);
|
| - }
|
| -}
|
| -
|
| -// static
|
| -void OmxVideoDecodeAccelerator::PreSandboxInitialization() {
|
| - DCHECK(!pre_sandbox_init_done_);
|
| - omx_handle = dlopen("libOmxCore.so", RTLD_NOW);
|
| - pre_sandbox_init_done_ = omx_handle != NULL;
|
| -}
|
| -
|
| -// static
|
| -bool OmxVideoDecodeAccelerator::PostSandboxInitialization() {
|
| - if (!pre_sandbox_init_done_)
|
| - return false;
|
| -
|
| - omx_init = reinterpret_cast<OMXInit>(dlsym(omx_handle, "OMX_Init"));
|
| - omx_gethandle =
|
| - reinterpret_cast<OMXGetHandle>(dlsym(omx_handle, "OMX_GetHandle"));
|
| - omx_get_components_of_role =
|
| - reinterpret_cast<OMXGetComponentsOfRole>(
|
| - dlsym(omx_handle, "OMX_GetComponentsOfRole"));
|
| - omx_free_handle =
|
| - reinterpret_cast<OMXFreeHandle>(dlsym(omx_handle, "OMX_FreeHandle"));
|
| - omx_deinit =
|
| - reinterpret_cast<OMXDeinit>(dlsym(omx_handle, "OMX_Deinit"));
|
| -
|
| - return (omx_init && omx_gethandle && omx_get_components_of_role &&
|
| - omx_free_handle && omx_deinit);
|
| -}
|
| -
|
| -// 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) {
|
| - // Called on the OMX thread.
|
| - OmxVideoDecodeAccelerator* decoder =
|
| - static_cast<OmxVideoDecodeAccelerator*>(priv_data);
|
| - DCHECK_EQ(component, decoder->component_handle_);
|
| - decoder->message_loop_->PostTask(FROM_HERE, base::Bind(
|
| - &OmxVideoDecodeAccelerator::EventHandlerCompleteTask,
|
| - decoder->weak_this(), event, data1, data2));
|
| - return OMX_ErrorNone;
|
| -}
|
| -
|
| -// static
|
| -OMX_ERRORTYPE OmxVideoDecodeAccelerator::EmptyBufferCallback(
|
| - OMX_HANDLETYPE component,
|
| - OMX_PTR priv_data,
|
| - OMX_BUFFERHEADERTYPE* buffer) {
|
| - TRACE_EVENT1("Video Decoder", "OVDA::EmptyBufferCallback",
|
| - "Buffer id", buffer->nTimeStamp);
|
| - // Called on the OMX thread.
|
| - OmxVideoDecodeAccelerator* decoder =
|
| - static_cast<OmxVideoDecodeAccelerator*>(priv_data);
|
| - DCHECK_EQ(component, decoder->component_handle_);
|
| - decoder->message_loop_->PostTask(FROM_HERE, base::Bind(
|
| - &OmxVideoDecodeAccelerator::EmptyBufferDoneTask, decoder->weak_this(),
|
| - buffer));
|
| - return OMX_ErrorNone;
|
| -}
|
| -
|
| -// static
|
| -OMX_ERRORTYPE OmxVideoDecodeAccelerator::FillBufferCallback(
|
| - OMX_HANDLETYPE component,
|
| - OMX_PTR priv_data,
|
| - OMX_BUFFERHEADERTYPE* buffer) {
|
| - media::Picture* picture =
|
| - reinterpret_cast<media::Picture*>(buffer->pAppPrivate);
|
| - int picture_buffer_id = picture ? picture->picture_buffer_id() : -1;
|
| - TRACE_EVENT2("Video Decoder", "OVDA::FillBufferCallback",
|
| - "Buffer id", buffer->nTimeStamp,
|
| - "Picture id", picture_buffer_id);
|
| - // Called on the OMX thread.
|
| - OmxVideoDecodeAccelerator* decoder =
|
| - static_cast<OmxVideoDecodeAccelerator*>(priv_data);
|
| - DCHECK_EQ(component, decoder->component_handle_);
|
| - decoder->message_loop_->PostTask(FROM_HERE, base::Bind(
|
| - &OmxVideoDecodeAccelerator::FillBufferDoneTask, decoder->weak_this(),
|
| - buffer));
|
| - return OMX_ErrorNone;
|
| -}
|
| -
|
| -bool OmxVideoDecodeAccelerator::CanFillBuffer() {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - const CurrentStateChange csc = current_state_change_;
|
| - const OMX_STATETYPE cs = client_state_;
|
| - return (csc != DESTROYING && csc != ERRORING && csc != RESETTING) &&
|
| - (cs == OMX_StateIdle || cs == OMX_StateExecuting || cs == OMX_StatePause);
|
| -}
|
| -
|
| -bool OmxVideoDecodeAccelerator::SendCommandToPort(
|
| - OMX_COMMANDTYPE cmd, int port_index) {
|
| - DCHECK_EQ(message_loop_, base::MessageLoop::current());
|
| - OMX_ERRORTYPE result = OMX_SendCommand(component_handle_,
|
| - cmd, port_index, 0);
|
| - RETURN_ON_OMX_FAILURE(result, "SendCommand() failed" << cmd,
|
| - PLATFORM_FAILURE, false);
|
| - return true;
|
| -}
|
| -
|
| -} // namespace content
|
|
|