diff options
author | fischman@chromium.org <fischman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-13 18:17:30 +0000 |
---|---|---|
committer | fischman@chromium.org <fischman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-13 18:17:30 +0000 |
commit | 436fd41df6378a81cbd8706eb9b1daed95cc7cab (patch) | |
tree | d40cbe4296f2b39dc9221e48a90ff7d53140003e /content | |
parent | 67351452e1e16cb9b01c609ce185a9e819cf2254 (diff) | |
download | chromium_src-436fd41df6378a81cbd8706eb9b1daed95cc7cab.zip chromium_src-436fd41df6378a81cbd8706eb9b1daed95cc7cab.tar.gz chromium_src-436fd41df6378a81cbd8706eb9b1daed95cc7cab.tar.bz2 |
Overhauled OmxVideoDecodeAccelerator's state machine implementation and related code.
This implementation makes explicit the transitions being effected and the states
expected to be involved in each transition, instead of the previous
implementation's use of implicit information (e.g. "we just went from Idle to
Loaded, therefore we must be Abort()'ing"). This is not only much more readable
but also enables implementation of Reset() (for Seek) and replaces Abort()
(which never had a well-defined purpose that was consistent in documentation and
implementation, nor a customer use-case).
DID some TODOs in OmxVideoDecodeAccelerator:
- Replaced error-code checking boilerplate w/ a failure-handling macro.
- Simplified the component-name-getting dance in CreateComponent().
Updated the PPAPI interface:
- Removed GetConfigs & AssignSysmemBuffers (neither of which ever came into its own)
- Replaced Abort with Reset, and added Destroy (which still nobody calls, but
that'll change shortly).
- Cleaned up the interface's documentation.
BUG=86122
TEST=OVDATest, gles2 sample plugin
Review URL: http://codereview.chromium.org/7338010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92383 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
11 files changed, 607 insertions, 644 deletions
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h index b035624..b30ab98 100644 --- a/content/common/gpu/gpu_messages.h +++ b/content/common/gpu/gpu_messages.h @@ -462,12 +462,6 @@ IPC_MESSAGE_ROUTED1(GpuTransportTextureHostMsg_TextureUpdated, // implementation REQUIRES that |tokens| be the first parameter of these // messages. -// Message to query configuration information from the GPU process. -IPC_SYNC_MESSAGE_CONTROL2_1(AcceleratedVideoDecoderMsg_GetConfigs, - gpu::ReadWriteTokens, /* tokens */ - std::vector<uint32>, /* Proto config */ - std::vector<uint32>) /* Matching configs */ - // Send input buffer for decoding. IPC_MESSAGE_ROUTED4(AcceleratedVideoDecoderMsg_Decode, gpu::ReadWriteTokens, /* tokens */ @@ -484,18 +478,6 @@ IPC_MESSAGE_ROUTED4(AcceleratedVideoDecoderMsg_AssignGLESBuffers, std::vector<uint32>, /* Texture ID */ std::vector<gfx::Size>) /* Size */ -// Sent from Renderer process to the GPU process to give the system memory -// buffers that the decoder will use for output. -// -// The length of the list of SharedMemoryHandles cannot exceed -// FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE; see -// ipc/file_descriptor_set_posix. -IPC_MESSAGE_ROUTED4(AcceleratedVideoDecoderMsg_AssignSysmemBuffers, - gpu::ReadWriteTokens, /* tokens */ - std::vector<int32>, /* Picture buffer ID */ - std::vector<base::SharedMemoryHandle>, /* Sysmem buffer */ - std::vector<gfx::Size>) /* Size */ - // Send from Renderer process to the GPU process to recycle the given picture // buffer for further decoding. IPC_MESSAGE_ROUTED2(AcceleratedVideoDecoderMsg_ReusePictureBuffer, @@ -506,13 +488,13 @@ IPC_MESSAGE_ROUTED2(AcceleratedVideoDecoderMsg_ReusePictureBuffer, IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderMsg_Flush, gpu::ReadWriteTokens) /* tokens */ -// Send abort request to the decoder. -IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderMsg_Abort, +// Send reset request to the decoder. +IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderMsg_Reset, gpu::ReadWriteTokens) /* tokens */ -// Destroy and release decoder asynchronously. -IPC_SYNC_MESSAGE_CONTROL1_0(AcceleratedVideoDecoderMsg_Destroy, - gpu::ReadWriteTokens) /* tokens */ +// Send destroy request to the decoder. +IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderMsg_Destroy, + gpu::ReadWriteTokens) /* tokens */ //------------------------------------------------------------------------------ // Accelerated Video Decoder Host Messages @@ -548,8 +530,11 @@ IPC_MESSAGE_ROUTED4(AcceleratedVideoDecoderHostMsg_PictureReady, // Confirm decoder has been flushed. IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_FlushDone) -// Confirm decoder has been aborted. -IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_AbortDone) +// Confirm decoder has been reset. +IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_ResetDone) + +// Confirm decoder has been destroyed. +IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_DestroyDone) // Decoder has faced end of stream marker in the stream. IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_EndOfStream) diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.cc b/content/common/gpu/media/gpu_video_decode_accelerator.cc index 15fb8e1..82fd408 100644 --- a/content/common/gpu/media/gpu_video_decode_accelerator.cc +++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc @@ -55,13 +55,12 @@ bool GpuVideoDecodeAccelerator::DeferMessageIfNeeded( const IPC::Message& msg, bool* deferred) { // Only consider deferring for message types that need it. switch (msg.type()) { - case AcceleratedVideoDecoderMsg_GetConfigs::ID: case AcceleratedVideoDecoderMsg_Decode::ID: case AcceleratedVideoDecoderMsg_AssignGLESBuffers::ID: - case AcceleratedVideoDecoderMsg_AssignSysmemBuffers::ID: case AcceleratedVideoDecoderMsg_ReusePictureBuffer::ID: case AcceleratedVideoDecoderMsg_Flush::ID: - case AcceleratedVideoDecoderMsg_Abort::ID: + case AcceleratedVideoDecoderMsg_Reset::ID: + case AcceleratedVideoDecoderMsg_Destroy::ID: break; default: return false; @@ -89,16 +88,14 @@ bool GpuVideoDecodeAccelerator::OnMessageReceived(const IPC::Message& msg) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(GpuVideoDecodeAccelerator, msg) - IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_GetConfigs, OnGetConfigs) IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Decode, OnDecode) IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_AssignGLESBuffers, OnAssignGLESBuffers) - IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_AssignSysmemBuffers, - OnAssignSysmemBuffers) IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_ReusePictureBuffer, OnReusePictureBuffer) IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Flush, OnFlush) - IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Abort, OnAbort) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Reset, OnReset) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Destroy, OnDestroy) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -158,16 +155,6 @@ void GpuVideoDecodeAccelerator::NotifyError( } } -void GpuVideoDecodeAccelerator::OnGetConfigs( - const gpu::ReadWriteTokens& /* tokens */, - const std::vector<uint32>& requested, std::vector<uint32>* matched) { - // TODO(fischman,vrk): this is borked; can't have a VDA before calling - // Initialize, but can't call Initialize until we have some configs! - if (!video_decode_accelerator_.get()) - return; - video_decode_accelerator_->GetConfigs(requested, matched); -} - void GpuVideoDecodeAccelerator::Initialize(const std::vector<uint32>& configs) { DCHECK(!video_decode_accelerator_.get()); #if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) @@ -213,15 +200,6 @@ void GpuVideoDecodeAccelerator::OnAssignGLESBuffers( video_decode_accelerator_->AssignGLESBuffers(buffers); } -void GpuVideoDecodeAccelerator::OnAssignSysmemBuffers( - const gpu::ReadWriteTokens& /* tokens */, - const std::vector<int32> buffer_ids, - const std::vector<base::SharedMemoryHandle> data, - const std::vector<gfx::Size> sizes) { - // TODO(vrk): Implement. - NOTIMPLEMENTED(); -} - void GpuVideoDecodeAccelerator::OnReusePictureBuffer( const gpu::ReadWriteTokens& /* tokens */, int32 picture_buffer_id) { @@ -235,10 +213,16 @@ void GpuVideoDecodeAccelerator::OnFlush( video_decode_accelerator_->Flush(); } -void GpuVideoDecodeAccelerator::OnAbort( +void GpuVideoDecodeAccelerator::OnReset( const gpu::ReadWriteTokens& /* tokens */) { DCHECK(video_decode_accelerator_.get()); - video_decode_accelerator_->Abort(); + video_decode_accelerator_->Reset(); +} + +void GpuVideoDecodeAccelerator::OnDestroy( + const gpu::ReadWriteTokens& /* tokens */) { + DCHECK(video_decode_accelerator_.get()); + video_decode_accelerator_->Destroy(); } void GpuVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( @@ -261,9 +245,14 @@ void GpuVideoDecodeAccelerator::NotifyFlushDone() { LOG(ERROR) << "Send(AcceleratedVideoDecoderHostMsg_FlushDone) failed"; } -void GpuVideoDecodeAccelerator::NotifyAbortDone() { - if (!Send(new AcceleratedVideoDecoderHostMsg_AbortDone(host_route_id_))) - LOG(ERROR) << "Send(AcceleratedVideoDecoderHostMsg_AbortDone) failed"; +void GpuVideoDecodeAccelerator::NotifyResetDone() { + if (!Send(new AcceleratedVideoDecoderHostMsg_ResetDone(host_route_id_))) + LOG(ERROR) << "Send(AcceleratedVideoDecoderHostMsg_ResetDone) failed"; +} + +void GpuVideoDecodeAccelerator::NotifyDestroyDone() { + if (!Send(new AcceleratedVideoDecoderHostMsg_DestroyDone(host_route_id_))) + LOG(ERROR) << "Send(AcceleratedVideoDecoderHostMsg_DestroyDone) failed"; } bool GpuVideoDecodeAccelerator::Send(IPC::Message* message) { diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.h b/content/common/gpu/media/gpu_video_decode_accelerator.h index 8fa944c..5d0302d 100644 --- a/content/common/gpu/media/gpu_video_decode_accelerator.h +++ b/content/common/gpu/media/gpu_video_decode_accelerator.h @@ -46,7 +46,8 @@ class GpuVideoDecodeAccelerator virtual void NotifyError(media::VideoDecodeAccelerator::Error error) OVERRIDE; virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id) OVERRIDE; virtual void NotifyFlushDone() OVERRIDE; - virtual void NotifyAbortDone() OVERRIDE; + virtual void NotifyResetDone() OVERRIDE; + virtual void NotifyDestroyDone() OVERRIDE; // Function to delegate sending to actual sender. virtual bool Send(IPC::Message* message); @@ -64,10 +65,6 @@ class GpuVideoDecodeAccelerator bool DeferMessageIfNeeded(const IPC::Message& msg, bool* deferred); // Handlers for IPC messages. - void OnGetConfigs( - const gpu::ReadWriteTokens& /* tokens */, - const std::vector<uint32>& config, - std::vector<uint32>* configs); void OnDecode( const gpu::ReadWriteTokens& /* tokens */, base::SharedMemoryHandle handle, int32 id, int32 size); @@ -76,16 +73,12 @@ class GpuVideoDecodeAccelerator const std::vector<int32>& buffer_ids, const std::vector<uint32>& texture_ids, const std::vector<gfx::Size>& sizes); - void OnAssignSysmemBuffers( - const gpu::ReadWriteTokens& /* tokens */, - const std::vector<int32> buffer_ids, - const std::vector<base::SharedMemoryHandle> data, - const std::vector<gfx::Size> sizes); void OnReusePictureBuffer( const gpu::ReadWriteTokens& /* tokens */, int32 picture_buffer_id); void OnFlush(const gpu::ReadWriteTokens& /* tokens */); - void OnAbort(const gpu::ReadWriteTokens& /* tokens */); + void OnReset(const gpu::ReadWriteTokens& /* tokens */); + void OnDestroy(const gpu::ReadWriteTokens& /* tokens */); // Pointer to the IPC message sender. IPC::Message::Sender* sender_; diff --git a/content/common/gpu/media/omx_video_decode_accelerator.cc b/content/common/gpu/media/omx_video_decode_accelerator.cc index 4a85f31..28614f2 100644 --- a/content/common/gpu/media/omx_video_decode_accelerator.cc +++ b/content/common/gpu/media/omx_video_decode_accelerator.cc @@ -18,18 +18,6 @@ typedef std::pair<scoped_ptr<base::SharedMemory>, int32> SharedMemoryAndId; enum { kNumPictureBuffers = 4 }; -// TODO(fischman): general cleanup opportunities in this file: -// - OMX_ERRORTYPE handling is boilerplatilicous. Would be nice to refactor out -// all the result=... and GetState business. -// - Many of the state-transition functions are phrased in terms of OMX -// Component state changes, but those don't necessarily map 1-1 with client -// needs. Make a unifying cleanup pass to make sure that there isn't more -// generality than we need, and that what we do need is expressed as concisely -// as possible. -// - LOG/CHECK statements that emit OMX_ERRORTYPE (result) should do so with the -// aid of std::hex and std::showbase for easier cross-referencing with -// OMX_Core.h. - // Open the libnvomx here for now. void* omx_handle = dlopen("libnvomx.so", RTLD_NOW); @@ -56,29 +44,44 @@ static bool AreOMXFunctionPointersInitialized() { omx_free_handle && omx_deinit); } +// 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)) { \ + LOG(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: " << std::hex << std::showbase << omx_result, \ + error, ret_val) + OmxVideoDecodeAccelerator::OmxVideoDecodeAccelerator( media::VideoDecodeAccelerator::Client* client) : message_loop_(MessageLoop::current()), component_handle_(NULL), + client_state_(OMX_StateMax), + current_state_change_(NO_TRANSITION), + saw_eos_during_flush_(false), input_buffer_count_(0), input_buffer_size_(0), input_port_(0), input_buffers_at_component_(0), output_port_(0), output_buffers_at_component_(0), - client_(client), - on_port_disable_event_func_(NULL), - on_port_enable_event_func_(NULL), - on_state_event_func_(NULL), - on_flush_event_func_(NULL), - on_buffer_flag_event_func_(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"; + client_(client) { + RETURN_ON_FAILURE(AreOMXFunctionPointersInitialized(), + "Failed to load openmax library", + VIDEODECODERERROR_UNSUPPORTED,); + RETURN_ON_OMX_FAILURE(omx_init(), "Failed to init OpenMAX core", + VIDEODECODERERROR_UNSUPPORTED,); } OmxVideoDecodeAccelerator::~OmxVideoDecodeAccelerator() { @@ -96,14 +99,12 @@ void OmxVideoDecodeAccelerator::SetEglState( egl_context_ = egl_context; } -bool OmxVideoDecodeAccelerator::GetConfigs( - const std::vector<uint32>& requested_configs, - std::vector<uint32>* matched_configs) { - DCHECK_EQ(message_loop_, MessageLoop::current()); +bool OmxVideoDecodeAccelerator::VerifyConfigs( + const std::vector<uint32>& configs) { size_t cur; - for (cur = 0; cur + 1 < requested_configs.size(); cur++) { - uint32 n = requested_configs[cur++]; - uint32 v = requested_configs[cur]; + for (cur = 0; cur + 1 < configs.size(); cur++) { + uint32 n = configs[cur++]; + uint32 v = configs[cur]; if ((n == media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC && v == media::VIDEOCODECFOURCC_H264) || (n == media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_BITRATE && @@ -126,13 +127,11 @@ bool OmxVideoDecodeAccelerator::GetConfigs( v == media::H264PROFILE_HIGH)) || (n == media::VIDEOATTRIBUTEKEY_VIDEOCOLORFORMAT && v == media::VIDEOCOLORFORMAT_RGBA)) { - matched_configs->push_back(n); - matched_configs->push_back(v); - } else { - return false; + continue; } + return false; } - return cur == requested_configs.size(); + return cur == configs.size(); } // This is to initialize the OMX data structures to default values. @@ -145,35 +144,17 @@ static void InitParam(const OmxVideoDecodeAccelerator& dec, T* param) { bool OmxVideoDecodeAccelerator::Initialize(const std::vector<uint32>& config) { DCHECK_EQ(message_loop_, MessageLoop::current()); - // Extract the required info from the configs. - // For now consider only what we care about. - std::vector<uint32> matched_configs; - GetConfigs(config, &matched_configs); - if (config != matched_configs) { - StopOnError(VIDEODECODERERROR_INVALIDINPUT); + RETURN_ON_FAILURE(VerifyConfigs(config), "Invalid config", + VIDEODECODERERROR_INVALIDINPUT, false); + if (!CreateComponent()) // Does its own RETURN_ON_FAILURE dances. return false; - } - client_state_ = OMX_StateLoaded; - if (!CreateComponent()) { - StopOnError(VIDEODECODERERROR_UNINITIALIZED); - return false; - } - // Transition component to Idle state - DCHECK(!on_state_event_func_); - on_state_event_func_ = - &OmxVideoDecodeAccelerator::OnStateChangeLoadedToIdle; - if (!TransitionToState(OMX_StateIdle)) { - LOG(ERROR) << "TransitionToState(OMX_StateIdle) error"; - StopOnError(VIDEODECODERERROR_UNINITIALIZED); - return false; - } + DCHECK_EQ(current_state_change_, NO_TRANSITION); + current_state_change_ = INITIALIZING; + BeginTransitionToState(OMX_StateIdle); - if (!AllocateInputBuffers()) { - LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error"; - StopOnError(VIDEODECODERERROR_MEMFAILURE); + if (!AllocateInputBuffers()) // Does its own RETURN_ON_FAILURE dances. return false; - } return true; } @@ -185,57 +166,29 @@ bool OmxVideoDecodeAccelerator::CreateComponent() { &OmxVideoDecodeAccelerator::EmptyBufferCallback, &OmxVideoDecodeAccelerator::FillBufferCallback }; - OMX_ERRORTYPE result = OMX_ErrorNone; - // 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 << ", " << result; - StopOnError(VIDEODECODERERROR_UNSUPPORTED); - 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(VIDEODECODERERROR_UNSUPPORTED); - return false; - } - - // 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(VIDEODECODERERROR_INSUFFICIENT_RESOURCES); - return false; - } + OMX_STRING role_name = const_cast<OMX_STRING>("video_decoder.avc"); + // Get the first component for this role and set the role on it. + OMX_U32 num_components = 1; + scoped_array<OMX_U8> component(new OMX_U8[OMX_MAX_STRINGNAME_SIZE]); + OMX_ERRORTYPE result = omx_get_components_of_role( + role_name, &num_components, reinterpret_cast<OMX_U8**>(&component)); + RETURN_ON_OMX_FAILURE(result, "Unsupport role: " << role_name, + VIDEODECODERERROR_UNSUPPORTED, false); + RETURN_ON_FAILURE(num_components == 1, + "No components for: " << role_name, + VIDEODECODERERROR_UNSUPPORTED, false); + + // Get the handle to the component. + result = omx_gethandle( + &component_handle_, reinterpret_cast<OMX_STRING>(component.get()), + this, &omx_accelerator_callbacks); + RETURN_ON_OMX_FAILURE(result, + "Failed to OMX_GetHandle on: " << component.get(), + VIDEODECODERERROR_INSUFFICIENT_RESOURCES, false); + client_state_ = OMX_StateLoaded; // Get the port information. This will obtain information about the number of // ports and index of the first port. @@ -243,12 +196,10 @@ bool OmxVideoDecodeAccelerator::CreateComponent() { 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: " - << result << ", " << port_param.nPorts; - StopOnError(VIDEODECODERERROR_INSUFFICIENT_RESOURCES); - return false; - } + RETURN_ON_FAILURE(result == OMX_ErrorNone && port_param.nPorts == 2, + "Failed to get Port Param: " << result << ", " + << port_param.nPorts, + VIDEODECODERERROR_INSUFFICIENT_RESOURCES, false); input_port_ = port_param.nStartPortNumber; output_port_ = input_port_ + 1; @@ -262,11 +213,8 @@ bool OmxVideoDecodeAccelerator::CreateComponent() { result = OMX_SetParameter(component_handle_, OMX_IndexParamStandardComponentRole, &role_type); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "Failed to Set Role"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return false; - } + RETURN_ON_OMX_FAILURE(result, "Failed to Set Role", + VIDEODECODERERROR_INVALIDINPUT, false); // Populate input-buffer-related members based on input port data. OMX_PARAM_PORTDEFINITIONTYPE port_format; @@ -275,16 +223,12 @@ bool OmxVideoDecodeAccelerator::CreateComponent() { result = OMX_GetParameter(component_handle_, OMX_IndexParamPortDefinition, &port_format); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return false; - } - if (OMX_DirInput != port_format.eDir) { - LOG(ERROR) << "Expected input port"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return false; - } + RETURN_ON_OMX_FAILURE(result, + "GetParameter(OMX_IndexParamPortDefinition) failed", + VIDEODECODERERROR_INVALIDINPUT, false); + RETURN_ON_FAILURE(OMX_DirInput == port_format.eDir, "Expected input port", + VIDEODECODERERROR_INVALIDINPUT, false); + input_buffer_count_ = port_format.nBufferCountActual; input_buffer_size_ = port_format.nBufferSize; @@ -294,16 +238,11 @@ bool OmxVideoDecodeAccelerator::CreateComponent() { result = OMX_GetParameter(component_handle_, OMX_IndexParamPortDefinition, &port_format); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return false; - } - if (OMX_DirOutput != port_format.eDir) { - LOG(ERROR) << "Expect Output Port"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return false; - } + RETURN_ON_OMX_FAILURE(result, + "GetParameter(OMX_IndexParamPortDefinition) failed", + VIDEODECODERERROR_INVALIDINPUT, false); + RETURN_ON_FAILURE(OMX_DirOutput == port_format.eDir, "Expect Output Port", + VIDEODECODERERROR_INVALIDINPUT, false); // Set output port parameters. port_format.nBufferCountActual = kNumPictureBuffers; @@ -316,11 +255,9 @@ bool OmxVideoDecodeAccelerator::CreateComponent() { result = OMX_SetParameter(component_handle_, OMX_IndexParamPortDefinition, &port_format); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) failed"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return false; - } + RETURN_ON_OMX_FAILURE(result, + "SetParameter(OMX_IndexParamPortDefinition) failed", + VIDEODECODERERROR_INVALIDINPUT, false); // Fill the component with fake output buffers. This seems to be required for // the component to move from Loaded to Idle. How bogus. @@ -328,12 +265,10 @@ bool OmxVideoDecodeAccelerator::CreateComponent() { OMX_BUFFERHEADERTYPE* buffer; result = OMX_UseBuffer(component_handle_, &buffer, output_port_, NULL, 0, reinterpret_cast<OMX_U8*>(0x1)); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "OMX_UseBuffer failed with: " << result; - return false; - } + RETURN_ON_OMX_FAILURE(result, "OMX_UseBuffer failed with: " << result, + VIDEODECODERERROR_INVALIDINPUT, false); buffer->pAppPrivate = NULL; - buffer->nTimeStamp = 0; + buffer->nTimeStamp = -1; buffer->nOutputPortIndex = output_port_; CHECK(fake_output_buffers_.insert(buffer).second); } @@ -346,8 +281,12 @@ void OmxVideoDecodeAccelerator::Decode( DCHECK_EQ(message_loop_, MessageLoop::current()); DCHECK(!free_input_buffers_.empty()); - if (!CanAcceptInput()) - return; + RETURN_ON_FAILURE(current_state_change_ == NO_TRANSITION && + (client_state_ == OMX_StateIdle || + client_state_ == OMX_StateExecuting), + "Call to Decode() during invalid state or transition:" + << current_state_change_ << ", " << client_state_, + VIDEODECODERERROR_UNSUPPORTED,); OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front(); free_input_buffers_.pop(); @@ -355,10 +294,10 @@ void OmxVideoDecodeAccelerator::Decode( // Setup |omx_buffer|. scoped_ptr<base::SharedMemory> shm( new base::SharedMemory(bitstream_buffer.handle(), true)); - if (!shm->Map(bitstream_buffer.size())) { - LOG(ERROR) << "Failed to SharedMemory::Map()."; - return; - } + RETURN_ON_FAILURE(shm->Map(bitstream_buffer.size()), + "Failed to SharedMemory::Map()", + VIDEODECODERERROR_UNSUPPORTED,); + SharedMemoryAndId* input_buffer_details = new SharedMemoryAndId(); input_buffer_details->first.reset(shm.release()); input_buffer_details->second = bitstream_buffer.id(); @@ -375,23 +314,20 @@ void OmxVideoDecodeAccelerator::Decode( omx_buffer->nTimeStamp = bitstream_buffer.id(); // 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(VIDEODECODERERROR_INVALIDINPUT); - return; - } + OMX_ERRORTYPE result = OMX_EmptyThisBuffer(component_handle_, omx_buffer); + RETURN_ON_OMX_FAILURE(result, + "OMX_EmptyThisBuffer() failed with result " << result, + VIDEODECODERERROR_INVALIDINPUT,); + input_buffers_at_component_++; } void OmxVideoDecodeAccelerator::AssignGLESBuffers( const std::vector<media::GLESBuffer>& buffers) { DCHECK_EQ(message_loop_, MessageLoop::current()); - if (!CanFillBuffer()) { - StopOnError(VIDEODECODERERROR_UNINITIALIZED); - return; - } + RETURN_ON_FAILURE(CanFillBuffer(), "Can't fill buffer", + VIDEODECODERERROR_UNSUPPORTED,); + CHECK_EQ(output_buffers_at_component_, 0); CHECK_EQ(fake_output_buffers_.size(), 0U); CHECK_EQ(pictures_.size(), 0U); @@ -408,169 +344,114 @@ void OmxVideoDecodeAccelerator::AssignGLESBuffers( return; // get all the buffers first. DCHECK_EQ(pictures_.size(), kNumPictureBuffers); - if (!AllocateOutputBuffers()) { - LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; - StopOnError(VIDEODECODERERROR_MEMFAILURE); + if (!AllocateOutputBuffers()) // Does its own RETURN_ON_FAILURE dances. return; - } - - DCHECK(!on_port_enable_event_func_); - on_port_enable_event_func_ = - &OmxVideoDecodeAccelerator::PortEnabledAfterSettingsChange; - ChangePort(OMX_CommandPortEnable, output_port_); -} -void OmxVideoDecodeAccelerator::AssignSysmemBuffers( - const std::vector<media::SysmemBuffer>& buffers) { - // We don't support decoding to system memory because we don't have HW that - // needs it yet. - NOTIMPLEMENTED(); + if (!SendCommandToPort(OMX_CommandPortEnable, output_port_)) + return; } void OmxVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { DCHECK_EQ(message_loop_, MessageLoop::current()); - // TODO(vhiremath@nvidia.com) Avoid leaking of the picture buffer. - if (!CanFillBuffer()) - return; + RETURN_ON_FAILURE(CanFillBuffer(), "Can't fill buffer", + VIDEODECODERERROR_UNSUPPORTED,); OutputPictureById::iterator it = pictures_.find(picture_buffer_id); - if (it == pictures_.end()) { - LOG(DFATAL) << "Missing picture buffer id: " << picture_buffer_id; - return; - } + RETURN_ON_FAILURE(it != pictures_.end(), + "Missing picture buffer id: " << picture_buffer_id, + VIDEODECODERERROR_UNSUPPORTED,); OutputPicture& output_picture = it->second; ++output_buffers_at_component_; OMX_ERRORTYPE result = OMX_FillThisBuffer(component_handle_, output_picture.omx_buffer_header); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << result; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return; - } + RETURN_ON_OMX_FAILURE(result, + "OMX_FillThisBuffer() failed with result " << result, + VIDEODECODERERROR_INVALIDINPUT,); } void OmxVideoDecodeAccelerator::Flush() { DCHECK_EQ(message_loop_, MessageLoop::current()); - OMX_STATETYPE il_state; - OMX_GetState(component_handle_, &il_state); - DCHECK_EQ(il_state, OMX_StateExecuting); - // Decode the pending data first. Then flush I/O ports. - if (il_state != OMX_StateExecuting) { - client_->NotifyFlushDone(); - return; - } - on_buffer_flag_event_func_ = &OmxVideoDecodeAccelerator::FlushBegin; + DCHECK_EQ(current_state_change_, NO_TRANSITION); + DCHECK_EQ(client_state_, OMX_StateExecuting); + current_state_change_ = FLUSHING; + // Cook up an empty buffer w/ EOS set and feed it to OMX. OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front(); free_input_buffers_.pop(); - 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(VIDEODECODERERROR_INVALIDINPUT); - return; - } + omx_buffer->nTimeStamp = -2; + OMX_ERRORTYPE result = OMX_EmptyThisBuffer(component_handle_, omx_buffer); + RETURN_ON_OMX_FAILURE(result, + "OMX_EmptyThisBuffer() failed with result " << result, + VIDEODECODERERROR_INVALIDINPUT,); input_buffers_at_component_++; } -void OmxVideoDecodeAccelerator::FlushBegin() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - VLOG(1) << "Starting actual flush for EOS"; - DCHECK(!on_state_event_func_); - on_state_event_func_ = &OmxVideoDecodeAccelerator::PauseFromExecuting; - TransitionToState(OMX_StatePause); -} - -void OmxVideoDecodeAccelerator::PauseFromExecuting(OMX_STATETYPE ignored) { +void OmxVideoDecodeAccelerator::OnReachedEOSInFlushing() { DCHECK_EQ(message_loop_, MessageLoop::current()); - on_state_event_func_ = NULL; - FlushIOPorts(); + BeginTransitionToState(OMX_StatePause); } void OmxVideoDecodeAccelerator::FlushIOPorts() { DCHECK_EQ(message_loop_, MessageLoop::current()); - // TODO(vhiremath@nvidia.com) review again for trick modes. - VLOG(1) << "FlushIOPorts"; // Flush input port first. - DCHECK(!on_flush_event_func_); - on_flush_event_func_ = &OmxVideoDecodeAccelerator::InputPortFlushDone; - OMX_ERRORTYPE result = OMX_SendCommand(component_handle_, - OMX_CommandFlush, - input_port_, 0); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "OMX_SendCommand(OMX_CommandFlush) failed"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); + if (!SendCommandToPort(OMX_CommandFlush, input_port_)) return; - } } -void OmxVideoDecodeAccelerator::InputPortFlushDone(int port) { +void OmxVideoDecodeAccelerator::InputPortFlushDone() { DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(port, input_port_); - VLOG(1) << "Input Port has been flushed"; DCHECK_EQ(input_buffers_at_component_, 0); - // Flush output port next. - DCHECK(!on_flush_event_func_); - on_flush_event_func_ = &OmxVideoDecodeAccelerator::OutputPortFlushDone; - if (OMX_ErrorNone != - OMX_SendCommand(component_handle_, - OMX_CommandFlush, - output_port_, 0)) { - LOG(ERROR) << "OMX_SendCommand(OMX_CommandFlush) failed"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); + if (!SendCommandToPort(OMX_CommandFlush, output_port_)) return; - } } -void OmxVideoDecodeAccelerator::OutputPortFlushDone(int port) { +void OmxVideoDecodeAccelerator::OutputPortFlushDone() { DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(port, output_port_); - - VLOG(1) << "Output Port has been flushed"; DCHECK_EQ(output_buffers_at_component_, 0); - client_state_ = OMX_StatePause; - client_->NotifyFlushDone(); + BeginTransitionToState(OMX_StateExecuting); } -void OmxVideoDecodeAccelerator::Abort() { +void OmxVideoDecodeAccelerator::Reset() { DCHECK_EQ(message_loop_, MessageLoop::current()); - // Abort() implies immediacy but Flush() actually decodes pending data first. - // TODO(vhiremath@nvidia.com) Fix the Abort to handle this immediacy. - ShutDownOMXFromExecuting(); - return; + DCHECK_EQ(current_state_change_, NO_TRANSITION); + DCHECK_EQ(client_state_, OMX_StateExecuting); + current_state_change_ = RESETTING; + BeginTransitionToState(OMX_StatePause); } -// Event callback during initialization to handle DoneStateSet to idle -void OmxVideoDecodeAccelerator::OnStateChangeLoadedToIdle(OMX_STATETYPE state) { +void OmxVideoDecodeAccelerator::Destroy() { DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(!on_state_event_func_); - 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; + DCHECK_EQ(current_state_change_, NO_TRANSITION); + DCHECK_EQ(client_state_, OMX_StateExecuting); + current_state_change_ = DESTROYING; + BeginTransitionToState(OMX_StateIdle); } -// Event callback during initialization to handle DoneStateSet to executing -void OmxVideoDecodeAccelerator::OnStateChangeIdleToExecuting( - OMX_STATETYPE state) { +void OmxVideoDecodeAccelerator::BeginTransitionToState( + OMX_STATETYPE new_state) { DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(OMX_StateExecuting, state); - DCHECK(!on_state_event_func_); - VLOG(1) << "OMX video decode engine is in Executing"; + DCHECK_NE(current_state_change_, NO_TRANSITION); + OMX_ERRORTYPE result = OMX_SendCommand( + component_handle_, OMX_CommandStateSet, new_state, 0); + RETURN_ON_OMX_FAILURE(result, "SendCommand(OMX_CommandStateSet) failed", + VIDEODECODERERROR_INVALIDINPUT,); +} +void OmxVideoDecodeAccelerator::OnReachedIdleInInitializing() { + DCHECK_EQ(client_state_, OMX_StateLoaded); + client_state_ = OMX_StateIdle; + 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 @@ -580,109 +461,94 @@ void OmxVideoDecodeAccelerator::OnStateChangeIdleToExecuting( it != fake_output_buffers_.end(); ++it) { OMX_BUFFERHEADERTYPE* buffer = *it; OMX_ERRORTYPE result = OMX_FillThisBuffer(component_handle_, buffer); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "OMX_FillThisBuffer() failed with: " << result; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return; - } + RETURN_ON_OMX_FAILURE(result, + "OMX_FillThisBuffer() failed with: " << result, + VIDEODECODERERROR_INVALIDINPUT,); ++output_buffers_at_component_; } client_->NotifyInitializeDone(); } -// Send state transition command to component. -bool OmxVideoDecodeAccelerator::TransitionToState(OMX_STATETYPE new_state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(on_state_event_func_); - OMX_ERRORTYPE result = OMX_SendCommand( - component_handle_, OMX_CommandStateSet, new_state, 0); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return false; - } - client_state_ = new_state; - return true; +void OmxVideoDecodeAccelerator::OnReachedPauseInFlushing() { + DCHECK_EQ(client_state_, OMX_StateExecuting); + client_state_ = OMX_StatePause; + FlushIOPorts(); } -void OmxVideoDecodeAccelerator::ShutDownOMXFromExecuting() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - VLOG(1) << "Deinit from Executing"; - DCHECK(!on_state_event_func_); - on_state_event_func_ = - &OmxVideoDecodeAccelerator::OnStateChangeExecutingToIdle; - TransitionToState(OMX_StateIdle); +void OmxVideoDecodeAccelerator::OnReachedExecutingInFlushing() { + DCHECK_EQ(client_state_, OMX_StatePause); + client_state_ = OMX_StateExecuting; + DCHECK(saw_eos_during_flush_); + saw_eos_during_flush_ = false; + current_state_change_ = NO_TRANSITION; + client_->NotifyFlushDone(); } -void OmxVideoDecodeAccelerator::OnStateChangeExecutingToIdle( - OMX_STATETYPE state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(state, OMX_StateIdle); - DCHECK(!on_state_event_func_); +void OmxVideoDecodeAccelerator::OnReachedPauseInResetting() { + DCHECK_EQ(client_state_, OMX_StateExecuting); + client_state_ = OMX_StatePause; + FlushIOPorts(); +} + +void OmxVideoDecodeAccelerator::OnReachedExecutingInResetting() { + DCHECK_EQ(client_state_, OMX_StatePause); + client_state_ = OMX_StateExecuting; + current_state_change_ = NO_TRANSITION; + client_->NotifyResetDone(); +} + +void OmxVideoDecodeAccelerator::OnReachedIdleInDestroying() { + DCHECK_EQ(client_state_, OMX_StateExecuting); + 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(). - VLOG(1) << "Deinit from Idle"; - on_state_event_func_ = - &OmxVideoDecodeAccelerator::OnStateChangeIdleToLoaded; - TransitionToState(OMX_StateLoaded); + BeginTransitionToState(OMX_StateLoaded); + // TODO(fischman): evaluate what these conditionals are doing. What happens + // if they're false?? if (!input_buffers_at_component_) FreeInputBuffers(); - if (!output_buffers_at_component_) FreeOutputBuffers(); } -void OmxVideoDecodeAccelerator::OnStateChangeIdleToLoaded(OMX_STATETYPE state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(state, OMX_StateLoaded); - DCHECK(!on_state_event_func_); - - VLOG(1) << "Idle to Loaded"; +void OmxVideoDecodeAccelerator::ShutdownComponent() { + OMX_ERRORTYPE result = omx_free_handle(component_handle_); + if (result != OMX_ErrorNone) + LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result; + component_handle_ = NULL; + omx_deinit(); + client_state_ = OMX_StateMax; +} - 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; - } +void OmxVideoDecodeAccelerator::OnReachedLoadedInDestroying() { + DCHECK_EQ(client_state_, OMX_StateIdle); client_state_ = OMX_StateLoaded; - omx_deinit(); - VLOG(1) << "OMX Deinit Clean exit done"; - client_->NotifyAbortDone(); + current_state_change_ = NO_TRANSITION; + ShutdownComponent(); + client_->NotifyDestroyDone(); +} + +void OmxVideoDecodeAccelerator::OnReachedInvalidInErroring() { + client_state_ = OMX_StateInvalid; + ShutdownComponent(); } void OmxVideoDecodeAccelerator::StopOnError( media::VideoDecodeAccelerator::Error error) { DCHECK_EQ(message_loop_, MessageLoop::current()); + current_state_change_ = ERRORING; - if (client_) - client_->NotifyError(error); + client_->NotifyError(error); - if (client_state_ == OMX_StateInvalid) + if (client_state_ == OMX_StateInvalid || client_state_ == OMX_StateMax) return; - client_state_ = OMX_StateInvalid; - if (!component_handle_) - return; - - OMX_STATETYPE il_state; - OMX_GetState(component_handle_, &il_state); - switch (il_state) { - case OMX_StateExecuting: - ShutDownOMXFromExecuting(); - return; - case OMX_StateIdle: - OnStateChangeExecutingToIdle(OMX_StateIdle); - return; - case OMX_StateLoaded: - OnStateChangeIdleToLoaded(OMX_StateLoaded); - return; - default: - LOG(ERROR) << "Invalid state: " - << il_state << " received in StopOnError()"; - return; - } + BeginTransitionToState(OMX_StateInvalid); } bool OmxVideoDecodeAccelerator::AllocateInputBuffers() { @@ -698,8 +564,9 @@ bool OmxVideoDecodeAccelerator::AllocateInputBuffers() { OMX_UseBuffer(component_handle_, &buffer, input_port_, NULL, /* pAppPrivate gets set in Decode(). */ 0, reinterpret_cast<OMX_U8*>(0x1)); - if (result != OMX_ErrorNone) - return false; + RETURN_ON_OMX_FAILURE(result, + "OMX_UseBuffer() Input buffer error", + VIDEODECODERERROR_MEMFAILURE, false); buffer->nInputPortIndex = input_port_; buffer->nOffset = 0; buffer->nFlags = 0; @@ -722,10 +589,8 @@ bool OmxVideoDecodeAccelerator::AllocateOutputBuffers() { OMX_ERRORTYPE result = OMX_UseEGLImage( component_handle_, omx_buffer, output_port_, &gles_buffer, it->second.egl_image); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "OMX_UseEGLImage failed with: " << result; - return false; - } + RETURN_ON_OMX_FAILURE(result, "OMX_UseEGLImage failed with: " << result, + VIDEODECODERERROR_MEMFAILURE, false); // Here we set a garbage bitstream buffer id, and then overwrite it before // passing to PictureReady. int garbage_bitstream_buffer_id = -1; @@ -745,11 +610,8 @@ void OmxVideoDecodeAccelerator::FreeInputBuffers() { 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) << "OMX_FreeBuffer failed with: " << result; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return; - } + RETURN_ON_OMX_FAILURE(result, "OMX_FreeBuffer failed with: " << result, + VIDEODECODERERROR_INVALIDINPUT,); } VLOG(1) << "Input buffers freed."; } @@ -765,11 +627,8 @@ void OmxVideoDecodeAccelerator::FreeOutputBuffers() { CHECK(omx_buffer); delete reinterpret_cast<media::Picture*>(omx_buffer->pAppPrivate); result = OMX_FreeBuffer(component_handle_, output_port_, omx_buffer); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "OMX_FreeBuffer failed with: " << result; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return; - } + RETURN_ON_OMX_FAILURE(result, "OMX_FreeBuffer failed with: " << result, + VIDEODECODERERROR_INVALIDINPUT,); texture2eglImage_translator.DestroyEglImage(egl_display_, it->second.egl_image); client_->DismissPictureBuffer(it->first); @@ -777,27 +636,15 @@ void OmxVideoDecodeAccelerator::FreeOutputBuffers() { pictures_.clear(); } -void OmxVideoDecodeAccelerator::OnIndexParamPortDefinitionChanged(int port) { +void OmxVideoDecodeAccelerator::OnOutputPortDisabled() { DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(port, output_port_); - DCHECK(!on_port_disable_event_func_); - on_port_disable_event_func_ = - &OmxVideoDecodeAccelerator::PortDisabledForSettingsChange; - ChangePort(OMX_CommandPortDisable, port); -} - -void OmxVideoDecodeAccelerator::PortDisabledForSettingsChange(int port) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(port, 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) << "OMX_GetParameter failed with: " << result; - return; - } + RETURN_ON_OMX_FAILURE(result, "OMX_GetParameter failed with: " << result, + VIDEODECODERERROR_UNSUPPORTED,); DCHECK_EQ(port_format.nBufferCountMin, kNumPictureBuffers); // TODO(fischman): to support mid-stream resize, need to free/dismiss any @@ -814,17 +661,15 @@ void OmxVideoDecodeAccelerator::PortDisabledForSettingsChange(int port) { PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE); } -void OmxVideoDecodeAccelerator::PortEnabledAfterSettingsChange(int port) { +void OmxVideoDecodeAccelerator::OnOutputPortEnabled() { DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(port, output_port_); if (!CanFillBuffer()) { - LOG(ERROR) << "Can't FillBuffer on port-enabled"; - StopOnError(VIDEODECODERERROR_INSUFFICIENT_RESOURCES); + StopOnError(VIDEODECODERERROR_UNSUPPORTED); // Should be STATE_ERROR. return; } - // Ask the decoder to fill the output buffers. + // Provide output buffers to decoder. for (OutputPictureById::iterator it = pictures_.begin(); it != pictures_.end(); ++it) { OMX_BUFFERHEADERTYPE* omx_buffer = it->second.omx_buffer_header; @@ -834,11 +679,9 @@ void OmxVideoDecodeAccelerator::PortEnabledAfterSettingsChange(int port) { 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(VIDEODECODERERROR_INSUFFICIENT_BUFFERS); - return; - } + RETURN_ON_OMX_FAILURE(result, + "OMX_FillThisBuffer() failed with result " << result, + VIDEODECODERERROR_INSUFFICIENT_BUFFERS,); } } @@ -852,23 +695,24 @@ void OmxVideoDecodeAccelerator::FillBufferDoneTask( CHECK_EQ(fake_output_buffers_.erase(buffer), 1U); OMX_ERRORTYPE result = OMX_FreeBuffer(component_handle_, output_port_, buffer); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "OMX_FreeBuffer failed with: " << result; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return; - } + RETURN_ON_OMX_FAILURE(result, "OMX_FreeBuffer failed with: " << result, + VIDEODECODERERROR_INVALIDINPUT,); return; } CHECK(!fake_output_buffers_.size()); - // During the transition from Paused to Idle (e.g. during Flush()) all + if (current_state_change_ == FLUSHING && + buffer->nFlags & OMX_BUFFERFLAG_EOS) { + DCHECK(!saw_eos_during_flush_); + saw_eos_during_flush_ = true; + } + + // During the transition from Executing to Idle, and during port-flushing, all // pictures are sent back through here. Avoid giving them to the client. - // TODO(fischman): this is a hokey way to detect this condition. The state - // transitions in this class need to be rethought, and this implemented more - // sanely. - if (buffer->nFlags & OMX_BUFFERFLAG_EOS || - on_flush_event_func_ == &OmxVideoDecodeAccelerator::InputPortFlushDone || - on_flush_event_func_ == &OmxVideoDecodeAccelerator::OutputPortFlushDone) { + // Also avoid sending the (fake) EOS buffer to the client. + if ((current_state_change_ != NO_TRANSITION && + current_state_change_ != FLUSHING) || + saw_eos_during_flush_) { return; } @@ -899,6 +743,59 @@ void OmxVideoDecodeAccelerator::EmptyBufferDoneTask( delete input_buffer_details; } +void OmxVideoDecodeAccelerator::DispatchStateReached(OMX_STATETYPE reached) { + DCHECK_EQ(message_loop_, 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; + } + case FLUSHING: + switch (reached) { + case OMX_StatePause: + OnReachedPauseInFlushing(); + return; + case OMX_StateExecuting: + OnReachedExecutingInFlushing(); + return; + default: + NOTREACHED() << "Unexpected state in FLUSHING: " << reached; + } + case RESETTING: + switch (reached) { + case OMX_StatePause: + OnReachedPauseInResetting(); + return; + case OMX_StateExecuting: + OnReachedExecutingInResetting(); + return; + default: + NOTREACHED() << "Unexpected state in RESETTING: " << reached; + } + case DESTROYING: + switch (reached) { + case OMX_StateIdle: + OnReachedIdleInDestroying(); + return; + case OMX_StateLoaded: + OnReachedLoadedInDestroying(); + return; + default: + NOTREACHED() << "Unexpected state in DESTROYING: " << reached; + } + default: + NOTREACHED() << "Unexpected state in " << current_state_change_ + << ": " << reached; + } +} + void OmxVideoDecodeAccelerator::EventHandlerCompleteTask(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { @@ -911,65 +808,66 @@ void OmxVideoDecodeAccelerator::EventHandlerCompleteTask(OMX_EVENTTYPE event, OMX_COMMANDTYPE cmd = static_cast<OMX_COMMANDTYPE>(data1); switch (cmd) { case OMX_CommandPortDisable: - DCHECK(on_port_disable_event_func_); - if (on_port_disable_event_func_) { - void (OmxVideoDecodeAccelerator::*func)(int) = NULL; - std::swap(func, on_port_disable_event_func_); - (this->*func)(static_cast<int>(data2)); - } + DCHECK_EQ(data2, output_port_); + OnOutputPortDisabled(); break; case OMX_CommandPortEnable: - DCHECK(on_port_enable_event_func_); - if (on_port_enable_event_func_) { - void (OmxVideoDecodeAccelerator::*func)(int) = NULL; - std::swap(func, on_port_enable_event_func_); - (this->*func)(static_cast<int>(data2)); - } + DCHECK_EQ(data2, output_port_); + OnOutputPortEnabled(); break; case OMX_CommandStateSet: - { - void (OmxVideoDecodeAccelerator::*func)(OMX_STATETYPE state) = NULL; - std::swap(func, on_state_event_func_); - (this->*func)(static_cast<OMX_STATETYPE>(data2)); - } + DispatchStateReached(static_cast<OMX_STATETYPE>(data2)); break; case OMX_CommandFlush: - { - void (OmxVideoDecodeAccelerator::*func)(int) = NULL; - std::swap(func, on_flush_event_func_); - (this->*func)(data2); - } + DCHECK(current_state_change_ == FLUSHING || + current_state_change_ == RESETTING || + current_state_change_ == DESTROYING); + if (data2 == input_port_) + InputPortFlushDone(); + else if (data2 == output_port_) + OutputPortFlushDone(); + else + NOTREACHED() << "Unexpected port flushed: " << data2; break; default: - LOG(ERROR) << "Unknown command completed\n" << data1; - break; + RETURN_ON_FAILURE(false, "Unknown command completed: " << data1, + VIDEODECODERERROR_HARDWARE,); } break; } case OMX_EventError: - StopOnError(VIDEODECODERERROR_HARDWARE); - break; + RETURN_ON_FAILURE(false, "EventError: " << data1, + VIDEODECODERERROR_HARDWARE,); case OMX_EventPortSettingsChanged: if (data2 == OMX_IndexParamPortDefinition) { - OnIndexParamPortDefinitionChanged(static_cast<int>(data1)); - } else if (data1 == static_cast<OMX_U32>(output_port_) && + 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. + // TODO(vjain): Handle video crop rect. } else { - LOG(ERROR) << "Unexpected EventPortSettingsChanged [data1:" - << data1 << " data2:" << data2 << "]"; + RETURN_ON_FAILURE(false, + "Unexpected EventPortSettingsChanged: " + << data1 << ", " << data2, + VIDEODECODERERROR_HARDWARE,); } break; case OMX_EventBufferFlag: - if (data1 == static_cast<OMX_U32>(output_port_)) { - void (OmxVideoDecodeAccelerator::*func)() = NULL; - std::swap(func, on_buffer_flag_event_func_); - (this->*func)(); + if (data1 == output_port_) { + DCHECK_EQ(current_state_change_, FLUSHING); + OnReachedEOSInFlushing(); + } else { + RETURN_ON_FAILURE(false, + "Unexpected OMX_EventBufferFlag: " + << data1 << ", " << data2, + VIDEODECODERERROR_HARDWARE,); } break; default: - LOG(ERROR) << "Event: " << event << "unhandled"; - break; + RETURN_ON_FAILURE(false, "Unexpected unhandled event: " << event, + VIDEODECODERERROR_HARDWARE,); } } @@ -1026,42 +924,21 @@ OMX_ERRORTYPE OmxVideoDecodeAccelerator::FillBufferCallback( return OMX_ErrorNone; } -bool OmxVideoDecodeAccelerator::CanAcceptInput() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - // 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() { DCHECK_EQ(message_loop_, MessageLoop::current()); - // Make sure component is in the executing state and end-of-stream - // has not been reached. - OMX_ERRORTYPE result; - OMX_STATETYPE il_state; - if (client_state_ == OMX_StateLoaded) - return false; - result = OMX_GetState(component_handle_, &il_state); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "OMX_GetState failed"; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return false; - } - return (il_state == OMX_StateExecuting); + return client_state_ == OMX_StateIdle || + client_state_ == OMX_StateExecuting || + client_state_ == OMX_StatePause; } -// Send command to disable/enable port. -void OmxVideoDecodeAccelerator::ChangePort( +bool OmxVideoDecodeAccelerator::SendCommandToPort( OMX_COMMANDTYPE cmd, int port_index) { DCHECK_EQ(message_loop_, MessageLoop::current()); OMX_ERRORTYPE result = OMX_SendCommand(component_handle_, cmd, port_index, 0); - if (result != OMX_ErrorNone) { - LOG(ERROR) << "SendCommand() failed" << cmd << ":" << result; - StopOnError(VIDEODECODERERROR_INVALIDINPUT); - return; - } + RETURN_ON_OMX_FAILURE(result, "SendCommand() failed" << cmd << ":" << result, + VIDEODECODERERROR_INVALIDINPUT, false); + return true; } DISABLE_RUNNABLE_METHOD_REFCOUNT(OmxVideoDecodeAccelerator); diff --git a/content/common/gpu/media/omx_video_decode_accelerator.h b/content/common/gpu/media/omx_video_decode_accelerator.h index c99eb3c..4c246d0 100644 --- a/content/common/gpu/media/omx_video_decode_accelerator.h +++ b/content/common/gpu/media/omx_video_decode_accelerator.h @@ -39,21 +39,31 @@ class OmxVideoDecodeAccelerator : public media::VideoDecodeAccelerator { virtual ~OmxVideoDecodeAccelerator(); // media::VideoDecodeAccelerator implementation. - bool GetConfigs(const std::vector<uint32>& requested_configs, - std::vector<uint32>* matched_configs) OVERRIDE; bool Initialize(const std::vector<uint32>& config) OVERRIDE; void Decode(const media::BitstreamBuffer& bitstream_buffer) OVERRIDE; virtual void AssignGLESBuffers( const std::vector<media::GLESBuffer>& buffers) OVERRIDE; - virtual void AssignSysmemBuffers( - const std::vector<media::SysmemBuffer>& buffers) OVERRIDE; void ReusePictureBuffer(int32 picture_buffer_id) OVERRIDE; void Flush() OVERRIDE; - void Abort() OVERRIDE; + void Reset() OVERRIDE; + void Destroy() OVERRIDE; void SetEglState(EGLDisplay egl_display, EGLContext egl_context); private: + // Because OMX state-transitions are described solely by the "state reached" + // (3.1.2.9.1, table 3-7 of the spec), we track what transition was requested + // using this enum. Note that it is an error to request a transition while + // |*this| is in any state other than NO_TRANSITION. + enum CurrentStateChange { + NO_TRANSITION, // Not in the middle of a transition. + INITIALIZING, + FLUSHING, + RESETTING, + DESTROYING, + ERRORING, // Trumps all other transitions; no recovery is possible. + }; + // Helper struct for keeping track of the relationship between an OMX output // buffer and the GLESBuffer it points to. struct OutputPicture { @@ -66,58 +76,77 @@ class OmxVideoDecodeAccelerator : public media::VideoDecodeAccelerator { }; typedef std::map<int32, OutputPicture> OutputPictureById; + // Verify that |config| is compatible with this class and hardware. + bool VerifyConfigs(const std::vector<uint32>& configs); + MessageLoop* message_loop_; OMX_HANDLETYPE component_handle_; // Create the Component for OMX. Handles all OMX initialization. bool CreateComponent(); + void ShutdownComponent(); // 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 OnStateChangeExecutingToIdle(OMX_STATETYPE state); - void OnStateChangeIdleToLoaded(OMX_STATETYPE state); - // Stop the components when error is detected. - void StopOnError(media::VideoDecodeAccelerator::Error error); - // Methods for shutdown - void PauseFromExecuting(OMX_STATETYPE ignored); + // Methods to handle OMX state transitions. See section 3.1.1.2 of the spec. + // Request transitioning OMX component to some other state. + void BeginTransitionToState(OMX_STATETYPE new_state); + // The callback received when the OMX component has transitioned. + void DispatchStateReached(OMX_STATETYPE reached); + // Callbacks handling transitioning to specific states during state changes. + // These follow a convention of OnReached<STATE>In<CurrentStateChange>(), + // requiring that each pair of <reached-state>/CurrentStateChange is unique + // (i.e. the source state is uniquely defined by the pair). + void OnReachedIdleInInitializing(); + void OnReachedExecutingInInitializing(); + void OnReachedPauseInFlushing(); + void OnReachedExecutingInFlushing(); + void OnReachedPauseInResetting(); + void OnReachedExecutingInResetting(); + void OnReachedIdleInDestroying(); + void OnReachedLoadedInDestroying(); + void OnReachedEOSInFlushing(); + void OnReachedInvalidInErroring(); + + // Port-flushing helpers. void FlushIOPorts(); - void InputPortFlushDone(int port); - void OutputPortFlushDone(int port); - void FlushBegin(); - void ShutDownOMXFromExecuting(); - - // 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(); + void InputPortFlushDone(); + void OutputPortFlushDone(); + + // Stop the component when any error is detected. + void StopOnError(media::VideoDecodeAccelerator::Error error); + + // Determine whether we can issue fill buffer to the decoder based on the + // current state (and outstanding state change) of the component. bool CanFillBuffer(); - void OnIndexParamPortDefinitionChanged(int port); + // Whenever port settings change, the first thing we must do is disable the // port (see Figure 3-18 of the OpenMAX IL spec linked to above). When the // port is disabled, the component will call us back here. We then re-enable // the port once we have textures, and that's the second method below. - void PortDisabledForSettingsChange(int port); - void PortEnabledAfterSettingsChange(int port); + void OnOutputPortDisabled(); + void OnOutputPortEnabled(); // IL-client state. OMX_STATETYPE client_state_; + // See comment on CurrentStateChange above. + CurrentStateChange current_state_change_; + // TODO(fischman): come up with a better scheme than this. There must be some + // way that OMX signals to its client that EmptyBufferDone/FillBufferDone + // callbacks are the result of port-flushing as opposed to normal operation. + bool saw_eos_during_flush_; // Following are input port related variables. int input_buffer_count_; int input_buffer_size_; - int input_port_; + OMX_U32 input_port_; int input_buffers_at_component_; // Following are output port related variables. - int output_port_; + OMX_U32 output_port_; int output_buffers_at_component_; // NOTE: someday there may be multiple contexts for a single decoder. But not @@ -153,15 +182,9 @@ class OmxVideoDecodeAccelerator : public media::VideoDecodeAccelerator { // Method to receive buffers from component's output port void FillBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer); - // 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_)(); + // Send a command to an OMX port. Return false on failure (after logging and + // setting |this| to ERRORING state). + bool SendCommandToPort(OMX_COMMANDTYPE cmd, int port_index); // Callback methods for the OMX component. // When these callbacks are received, the diff --git a/content/common/gpu/media/omx_video_decode_accelerator_unittest.cc b/content/common/gpu/media/omx_video_decode_accelerator_unittest.cc index b47c088..6a6d35c 100644 --- a/content/common/gpu/media/omx_video_decode_accelerator_unittest.cc +++ b/content/common/gpu/media/omx_video_decode_accelerator_unittest.cc @@ -348,7 +348,7 @@ enum ClientState { CS_INITIALIZED, CS_FLUSHED, CS_DONE, - CS_ABORTED, + CS_RESET, CS_ERROR, CS_DESTROYED, CS_MAX, // Must be last entry. @@ -420,7 +420,8 @@ class EglRenderingVDAClient : public VideoDecodeAccelerator::Client { virtual void NotifyEndOfStream(); virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id); virtual void NotifyFlushDone(); - virtual void NotifyAbortDone(); + virtual void NotifyResetDone(); + virtual void NotifyDestroyDone(); virtual void NotifyError(VideoDecodeAccelerator::Error error); // Simple getters for inspecting the state of the Client. @@ -546,12 +547,15 @@ void EglRenderingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) { } void EglRenderingVDAClient::PictureReady(const media::Picture& picture) { + // We shouldn't be getting pictures delivered after Reset has completed. + DCHECK_LT(state_, CS_RESET); + if (!decoder_.get()) return; last_frame_delivered_ticks_ = base::TimeTicks::Now(); // Because we feed the decoder a limited number of NALUs at a time, we can be - // sure each the bitstream buffer from which a frame comes has a limited + // sure that the bitstream buffer from which a frame comes has a limited // range. Assert that. CHECK_GE((picture.bitstream_buffer_id() + 1) * num_NALUs_per_decode_, num_decoded_frames_); @@ -588,11 +592,20 @@ void EglRenderingVDAClient::NotifyFlushDone() { SetState(CS_FLUSHED); if (!decoder_.get()) return; - decoder_->Abort(); + decoder_->Reset(); +} + +void EglRenderingVDAClient::NotifyResetDone() { + if (!decoder_.get()) + return; + SetState(CS_RESET); + if (!decoder_.get()) + return; + decoder_->Destroy(); } -void EglRenderingVDAClient::NotifyAbortDone() { - SetState(CS_ABORTED); +void EglRenderingVDAClient::NotifyDestroyDone() { + SetState(CS_DESTROYED); } void EglRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) { @@ -680,6 +693,18 @@ class OmxVideoDecodeAcceleratorTest : public ::testing::TestWithParam<Tuple3<int, int, ClientState> > { }; +// Wait for |note| to report a state and if it's not |expected_state| then +// assert |client| has deleted its decoder. +static void AssertWaitForStateOrDeleted(ClientStateNotification* note, + EglRenderingVDAClient* client, + ClientState expected_state) { + ClientState state = note->Wait(); + if (state == expected_state) return; + ASSERT_TRUE(client->decoder_deleted()) + << "Decoder not deleted but Wait() returned " << state + << ", instead of " << expected_state; +} + // Test the most straightforward case possible: data is decoded from a single // chunk and rendered to the screen. TEST_P(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) { @@ -743,11 +768,14 @@ TEST_P(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) { ASSERT_EQ(note->Wait(), CS_INITIALIZED); // InitializeDone kicks off decoding inside the client, so we just need to // wait for Flush. - if (note->Wait() != CS_FLUSHED) - ASSERT_TRUE(clients[i]->decoder_deleted()); - // FlushDone requests Abort(). - if (note->Wait() != CS_ABORTED) - ASSERT_TRUE(clients[i]->decoder_deleted()); + ASSERT_NO_FATAL_FAILURE( + AssertWaitForStateOrDeleted(note, clients[i], CS_FLUSHED)); + // FlushDone requests Reset(). + ASSERT_NO_FATAL_FAILURE( + AssertWaitForStateOrDeleted(note, clients[i], CS_RESET)); + // ResetDone requests Destroy(). + ASSERT_NO_FATAL_FAILURE( + AssertWaitForStateOrDeleted(note, clients[i], CS_DESTROYED)); } // Finally assert that decoding went as expected. for (size_t i = 0; i < num_concurrent_decoders; ++i) { @@ -791,7 +819,7 @@ INSTANTIATE_TEST_CASE_P( DISABLED_TearDownTiming, OmxVideoDecodeAcceleratorTest, ::testing::Values( MakeTuple(1, 1, CS_DECODER_SET), MakeTuple(1, 1, CS_INITIALIZED), - MakeTuple(1, 1, CS_FLUSHED), MakeTuple(1, 1, CS_ABORTED), + MakeTuple(1, 1, CS_FLUSHED), MakeTuple(1, 1, CS_RESET), MakeTuple(1, 1, static_cast<ClientState>(-1)), MakeTuple(1, 1, static_cast<ClientState>(-10)), MakeTuple(1, 1, static_cast<ClientState>(-100)))); @@ -804,10 +832,10 @@ INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P( DecodeVariations, OmxVideoDecodeAcceleratorTest, ::testing::Values( - MakeTuple(1, 1, CS_ABORTED), MakeTuple(1, 3, CS_ABORTED), - MakeTuple(2, 1, CS_ABORTED), MakeTuple(3, 1, CS_ABORTED), - MakeTuple(5, 1, CS_ABORTED), MakeTuple(8, 1, CS_ABORTED), - MakeTuple(15, 1, CS_ABORTED))); + MakeTuple(1, 1, CS_DESTROYED), MakeTuple(1, 3, CS_DESTROYED), + MakeTuple(2, 1, CS_DESTROYED), MakeTuple(3, 1, CS_DESTROYED), + MakeTuple(5, 1, CS_DESTROYED), MakeTuple(8, 1, CS_DESTROYED), + MakeTuple(15, 1, CS_DESTROYED))); // TODO(fischman, vrk): add more tests! In particular: // - Test life-cycle: Seek/Stop/Pause/Play/RePlay for a single decoder. diff --git a/content/renderer/gpu/gpu_video_decode_accelerator_host.cc b/content/renderer/gpu/gpu_video_decode_accelerator_host.cc index c7b0eb9..7c2576b 100644 --- a/content/renderer/gpu/gpu_video_decode_accelerator_host.cc +++ b/content/renderer/gpu/gpu_video_decode_accelerator_host.cc @@ -57,8 +57,10 @@ bool GpuVideoDecodeAcceleratorHost::OnMessageReceived(const IPC::Message& msg) { OnPictureReady) IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_FlushDone, OnFlushDone) - IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_AbortDone, - OnAbortDone) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_ResetDone, + OnResetDone) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_DestroyDone, + OnDestroyDone) IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_EndOfStream, OnEndOfStream) IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_ErrorNotification, @@ -89,14 +91,6 @@ gpu::ReadWriteTokens GpuVideoDecodeAcceleratorHost::SyncTokens() { return gpu::ReadWriteTokens(read, written); } -bool GpuVideoDecodeAcceleratorHost::GetConfigs( - const std::vector<uint32>& requested_configs, - std::vector<uint32>* matched_configs) { - // TODO(vrk): Need to rethink GetConfigs. - NOTIMPLEMENTED(); - return true; -} - bool GpuVideoDecodeAcceleratorHost::Initialize( const std::vector<uint32>& configs) { NOTREACHED(); @@ -128,13 +122,6 @@ void GpuVideoDecodeAcceleratorHost::AssignGLESBuffers( command_buffer_route_id_, SyncTokens(), buffer_ids, texture_ids, sizes)); } -void GpuVideoDecodeAcceleratorHost::AssignSysmemBuffers( - const std::vector<media::SysmemBuffer>& buffers) { - DCHECK(CalledOnValidThread()); - // TODO(vrk): Implement. - NOTIMPLEMENTED(); -} - void GpuVideoDecodeAcceleratorHost::ReusePictureBuffer( int32 picture_buffer_id) { DCHECK(CalledOnValidThread()); @@ -148,9 +135,19 @@ void GpuVideoDecodeAcceleratorHost::Flush() { command_buffer_route_id_, SyncTokens())); } -void GpuVideoDecodeAcceleratorHost::Abort() { +void GpuVideoDecodeAcceleratorHost::Reset() { DCHECK(CalledOnValidThread()); - Send(new AcceleratedVideoDecoderMsg_Abort( + if (!ipc_sender_->Send(new AcceleratedVideoDecoderMsg_Reset( + command_buffer_route_id_, SyncTokens()))) { + LOG(ERROR) << "Send(AcceleratedVideoDecoderMsg_Reset) failed"; + // TODO(fischman/vrk): signal error to client. + return; + } +} + +void GpuVideoDecodeAcceleratorHost::Destroy() { + DCHECK(CalledOnValidThread()); + Send(new AcceleratedVideoDecoderMsg_Destroy( command_buffer_route_id_, SyncTokens())); } @@ -207,9 +204,14 @@ void GpuVideoDecodeAcceleratorHost::OnFlushDone() { client_->NotifyFlushDone(); } -void GpuVideoDecodeAcceleratorHost::OnAbortDone() { +void GpuVideoDecodeAcceleratorHost::OnResetDone() { + DCHECK(CalledOnValidThread()); + client_->NotifyResetDone(); +} + +void GpuVideoDecodeAcceleratorHost::OnDestroyDone() { DCHECK(CalledOnValidThread()); - client_->NotifyAbortDone(); + client_->NotifyDestroyDone(); } void GpuVideoDecodeAcceleratorHost::OnEndOfStream() { diff --git a/content/renderer/gpu/gpu_video_decode_accelerator_host.h b/content/renderer/gpu/gpu_video_decode_accelerator_host.h index a65255f..9c304ce 100644 --- a/content/renderer/gpu/gpu_video_decode_accelerator_host.h +++ b/content/renderer/gpu/gpu_video_decode_accelerator_host.h @@ -40,18 +40,14 @@ class GpuVideoDecodeAcceleratorHost virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; // media::VideoDecodeAccelerator implementation. - virtual bool GetConfigs( - const std::vector<uint32>& requested_configs, - std::vector<uint32>* matched_configs) OVERRIDE; virtual bool Initialize(const std::vector<uint32>& configs) OVERRIDE; virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) OVERRIDE; virtual void AssignGLESBuffers( const std::vector<media::GLESBuffer>& buffers) OVERRIDE; - virtual void AssignSysmemBuffers( - const std::vector<media::SysmemBuffer>& buffers) OVERRIDE; virtual void ReusePictureBuffer(int32 picture_buffer_id) OVERRIDE; virtual void Flush() OVERRIDE; - virtual void Abort() OVERRIDE; + virtual void Reset() OVERRIDE; + virtual void Destroy() OVERRIDE; private: // Insert a token into the command buffer and return a token-pair suitable for @@ -70,7 +66,8 @@ class GpuVideoDecodeAcceleratorHost const gfx::Size& visible_size, const gfx::Size& decoded_size); void OnFlushDone(); - void OnAbortDone(); + void OnResetDone(); + void OnDestroyDone(); void OnEndOfStream(); void OnErrorNotification(uint32 error); diff --git a/content/renderer/gpu/gpu_video_service_host.cc b/content/renderer/gpu/gpu_video_service_host.cc new file mode 100644 index 0000000..cb43332 --- /dev/null +++ b/content/renderer/gpu/gpu_video_service_host.cc @@ -0,0 +1,76 @@ +// 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/renderer/gpu/gpu_video_service_host.h" + +#include "content/common/gpu/gpu_messages.h" +#include "content/renderer/gpu/gpu_video_decode_accelerator_host.h" +#include "content/renderer/render_thread.h" +#include "media/video/video_decode_accelerator.h" + +GpuVideoServiceHost::GpuVideoServiceHost() + : channel_(NULL), + next_decoder_host_id_(0) { + DCHECK(RenderThread::current()); + DCHECK_EQ(RenderThread::current()->message_loop(), MessageLoop::current()); +} + +GpuVideoServiceHost::~GpuVideoServiceHost() { +} + +void GpuVideoServiceHost::set_channel(IPC::SyncChannel* channel) { + DCHECK(CalledOnValidThread()); + DCHECK(!channel_); + channel_ = channel; + if (!on_initialized_.is_null()) + on_initialized_.Run(); +} + +bool GpuVideoServiceHost::OnMessageReceived(const IPC::Message& msg) { + DCHECK(CalledOnValidThread()); + if (!channel_) + return false; + switch (msg.type()) { + case AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed::ID: + case AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers::ID: + case AcceleratedVideoDecoderHostMsg_InitializeDone::ID: + case AcceleratedVideoDecoderHostMsg_DismissPictureBuffer::ID: + case AcceleratedVideoDecoderHostMsg_PictureReady::ID: + case AcceleratedVideoDecoderHostMsg_FlushDone::ID: + case AcceleratedVideoDecoderHostMsg_ResetDone::ID: + case AcceleratedVideoDecoderHostMsg_DestroyDone::ID: + case AcceleratedVideoDecoderHostMsg_EndOfStream::ID: + case AcceleratedVideoDecoderHostMsg_ErrorNotification::ID: + if (router_.RouteMessage(msg)) + return true; + LOG(ERROR) << "AcceleratedVideoDecoderHostMsg cannot be dispatched."; + default: + return false; + } +} + +void GpuVideoServiceHost::OnChannelError() { + DCHECK(CalledOnValidThread()); + channel_ = NULL; +} + +void GpuVideoServiceHost::SetOnInitialized( + const base::Closure& on_initialized) { + DCHECK(CalledOnValidThread()); + DCHECK(on_initialized_.is_null()); + on_initialized_ = on_initialized; + if (channel_) + on_initialized.Run(); +} + +GpuVideoDecodeAcceleratorHost* GpuVideoServiceHost::CreateVideoAccelerator( + media::VideoDecodeAccelerator::Client* client, + int32 command_buffer_route_id, + gpu::CommandBufferHelper* cmd_buffer_helper) { + DCHECK(CalledOnValidThread()); + DCHECK(channel_); + return new GpuVideoDecodeAcceleratorHost( + &router_, channel_, next_decoder_host_id_++, + command_buffer_route_id, cmd_buffer_helper, client); +} diff --git a/content/renderer/pepper_platform_video_decoder_impl.cc b/content/renderer/pepper_platform_video_decoder_impl.cc index 4243330..9c8b247 100644 --- a/content/renderer/pepper_platform_video_decoder_impl.cc +++ b/content/renderer/pepper_platform_video_decoder_impl.cc @@ -27,14 +27,6 @@ PlatformVideoDecoderImpl::PlatformVideoDecoderImpl( PlatformVideoDecoderImpl::~PlatformVideoDecoderImpl() {} -bool PlatformVideoDecoderImpl::GetConfigs( - const std::vector<uint32>& requested_configs, - std::vector<uint32>* matched_configs) { - // TODO(vrk): Implement. - NOTIMPLEMENTED(); - return true; -} - bool PlatformVideoDecoderImpl::Initialize(const std::vector<uint32>& configs) { // TODO(vrk): Support multiple decoders. if (decoder_) @@ -71,12 +63,6 @@ void PlatformVideoDecoderImpl::AssignGLESBuffers( decoder_->AssignGLESBuffers(buffers); } -void PlatformVideoDecoderImpl::AssignSysmemBuffers( - const std::vector<media::SysmemBuffer>& buffers) { - DCHECK(decoder_); - decoder_->AssignSysmemBuffers(buffers); -} - void PlatformVideoDecoderImpl::ReusePictureBuffer( int32 picture_buffer_id) { DCHECK(decoder_); @@ -88,9 +74,14 @@ void PlatformVideoDecoderImpl::Flush() { decoder_->Flush(); } -void PlatformVideoDecoderImpl::Abort() { +void PlatformVideoDecoderImpl::Reset() { DCHECK(decoder_); - decoder_->Abort(); + decoder_->Reset(); +} + +void PlatformVideoDecoderImpl::Destroy() { + DCHECK(decoder_); + decoder_->Destroy(); } void PlatformVideoDecoderImpl::NotifyEndOfStream() { @@ -138,7 +129,12 @@ void PlatformVideoDecoderImpl::NotifyFlushDone() { client_->NotifyFlushDone(); } -void PlatformVideoDecoderImpl::NotifyAbortDone() { +void PlatformVideoDecoderImpl::NotifyResetDone() { + DCHECK_EQ(RenderThread::current()->message_loop(), MessageLoop::current()); + client_->NotifyResetDone(); +} + +void PlatformVideoDecoderImpl::NotifyDestroyDone() { DCHECK_EQ(RenderThread::current()->message_loop(), MessageLoop::current()); - client_->NotifyAbortDone(); + client_->NotifyDestroyDone(); } diff --git a/content/renderer/pepper_platform_video_decoder_impl.h b/content/renderer/pepper_platform_video_decoder_impl.h index fd168a5..b4d0d03 100644 --- a/content/renderer/pepper_platform_video_decoder_impl.h +++ b/content/renderer/pepper_platform_video_decoder_impl.h @@ -28,19 +28,15 @@ class PlatformVideoDecoderImpl virtual ~PlatformVideoDecoderImpl(); // PlatformVideoDecoder implementation. - virtual bool GetConfigs( - const std::vector<uint32>& requested_configs, - std::vector<uint32>* matched_configs) OVERRIDE; virtual bool Initialize(const std::vector<uint32>& configs) OVERRIDE; virtual void Decode( const media::BitstreamBuffer& bitstream_buffer) OVERRIDE; virtual void AssignGLESBuffers( const std::vector<media::GLESBuffer>& buffers) OVERRIDE; - virtual void AssignSysmemBuffers( - const std::vector<media::SysmemBuffer>& buffers) OVERRIDE; virtual void ReusePictureBuffer(int32 picture_buffer_id); virtual void Flush() OVERRIDE; - virtual void Abort() OVERRIDE; + virtual void Reset() OVERRIDE; + virtual void Destroy() OVERRIDE; // VideoDecodeAccelerator::Client implementation. virtual void ProvidePictureBuffers( @@ -55,7 +51,8 @@ class PlatformVideoDecoderImpl media::VideoDecodeAccelerator::Error error) OVERRIDE; virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id) OVERRIDE; virtual void NotifyFlushDone() OVERRIDE; - virtual void NotifyAbortDone() OVERRIDE; + virtual void NotifyResetDone() OVERRIDE; + virtual void NotifyDestroyDone() OVERRIDE; private: // Client lifetime must exceed lifetime of this class. |