blob: bb814f6c3334e2455e427dc80ba16695c861f3f1 [file] [log] [blame]
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/raspi/shared/open_max/open_max_component.h"
#include <algorithm>
#include "starboard/configuration.h"
#include "starboard/once.h"
#include "starboard/thread.h"
namespace starboard {
namespace raspi {
namespace shared {
namespace open_max {
namespace {
const int kInvalidPort = -1;
OMX_INDEXTYPE kPortTypes[] = {OMX_IndexParamAudioInit, OMX_IndexParamVideoInit,
OMX_IndexParamImageInit, OMX_IndexParamOtherInit};
SbOnceControl s_open_max_initialization_once = SB_ONCE_INITIALIZER;
void DoInitializeOpenMax() {
OMX_ERRORTYPE error = OMX_Init();
SB_DCHECK(error == OMX_ErrorNone);
}
void InitializeOpenMax() {
bool initialized =
SbOnce(&s_open_max_initialization_once, DoInitializeOpenMax);
SB_DCHECK(initialized);
}
} // namespace
OpenMaxComponent::OpenMaxComponent(const char* name)
: condition_variable_(mutex_),
handle_(NULL),
input_port_(kInvalidPort),
output_port_(kInvalidPort),
output_setting_changed_(false),
output_port_enabled_(false) {
InitializeOpenMax();
OMX_CALLBACKTYPE callbacks;
callbacks.EventHandler = OpenMaxComponent::EventHandler;
callbacks.EmptyBufferDone = OpenMaxComponent::EmptyBufferDone;
callbacks.FillBufferDone = OpenMaxComponent::FillBufferDone;
OMX_ERRORTYPE error =
OMX_GetHandle(&handle_, const_cast<char*>(name), this, &callbacks);
SB_DCHECK(error == OMX_ErrorNone);
for (size_t i = 0; i < SB_ARRAY_SIZE(kPortTypes); ++i) {
OMX_PORT_PARAM_TYPE port;
port.nSize = sizeof(OMX_PORT_PARAM_TYPE);
port.nVersion.nVersion = OMX_VERSION;
error = OMX_GetParameter(handle_, kPortTypes[i], &port);
if (error == OMX_ErrorNone && port.nPorts == 2) {
input_port_ = port.nStartPortNumber;
output_port_ = input_port_ + 1;
SendCommandAndWaitForCompletion(OMX_CommandPortDisable, input_port_);
SendCommandAndWaitForCompletion(OMX_CommandPortDisable, output_port_);
break;
}
}
SB_CHECK(input_port_ != kInvalidPort);
SB_CHECK(output_port_ != kInvalidPort);
SB_DLOG(INFO) << "Opened \"" << name << "\" with port " << input_port_
<< " and " << output_port_;
SendCommandAndWaitForCompletion(OMX_CommandStateSet, OMX_StateIdle);
}
OpenMaxComponent::~OpenMaxComponent() {
if (!handle_) {
return;
}
SendCommandAndWaitForCompletion(OMX_CommandStateSet, OMX_StateIdle);
SendCommandAndWaitForCompletion(OMX_CommandFlush, input_port_);
SendCommandAndWaitForCompletion(OMX_CommandFlush, output_port_);
SendCommand(OMX_CommandPortDisable, input_port_);
for (size_t i = 0; i < input_buffers_.size(); ++i) {
OMX_ERRORTYPE error =
OMX_FreeBuffer(handle_, input_port_, input_buffers_[i]);
SB_DCHECK(error == OMX_ErrorNone);
}
WaitForCommandCompletion();
SendCommand(OMX_CommandPortDisable, output_port_);
for (size_t i = 0; i < output_buffers_.size(); ++i) {
OMX_ERRORTYPE error =
OMX_FreeBuffer(handle_, output_port_, output_buffers_[i]);
SB_DCHECK(error == OMX_ErrorNone);
}
output_buffers_.clear();
WaitForCommandCompletion();
SendCommandAndWaitForCompletion(OMX_CommandStateSet, OMX_StateLoaded);
OMX_FreeHandle(handle_);
}
void OpenMaxComponent::Start() {
EnableInputPortAndAllocateBuffers();
SendCommandAndWaitForCompletion(OMX_CommandStateSet, OMX_StateExecuting);
}
void OpenMaxComponent::Flush() {
SendCommandAndWaitForCompletion(OMX_CommandFlush, input_port_);
SendCommandAndWaitForCompletion(OMX_CommandFlush, output_port_);
}
void OpenMaxComponent::WriteData(const void* data,
size_t size,
SbTime timestamp) {
size_t offset = 0;
while (offset != size) {
OMX_BUFFERHEADERTYPE* buffer_header = GetUnusedInputBuffer();
int size_to_append = std::min(size - offset, buffer_header->nAllocLen);
buffer_header->nOffset = 0;
buffer_header->nFilledLen = size_to_append;
buffer_header->nFlags = 0;
buffer_header->nTimeStamp.nLowPart = timestamp;
buffer_header->nTimeStamp.nHighPart = timestamp >> 32;
memcpy(buffer_header->pBuffer, (const char*)data + offset, size_to_append);
offset += size_to_append;
OMX_ERRORTYPE error = OMX_EmptyThisBuffer(handle_, buffer_header);
SB_DCHECK(error == OMX_ErrorNone);
}
}
void OpenMaxComponent::WriteEOS() {
OMX_BUFFERHEADERTYPE* buffer_header = GetUnusedInputBuffer();
buffer_header->nOffset = 0;
buffer_header->nFilledLen = 0;
buffer_header->nFlags = OMX_BUFFERFLAG_EOS;
OMX_ERRORTYPE error = OMX_EmptyThisBuffer(handle_, buffer_header);
SB_DCHECK(error == OMX_ErrorNone);
}
OMX_BUFFERHEADERTYPE* OpenMaxComponent::PeekNextOutputBuffer() {
{
ScopedLock scoped_lock(mutex_);
if (!output_setting_changed_) {
return NULL;
}
}
if (!output_port_enabled_) {
EnableOutputPortAndAllocateBuffer();
}
ScopedLock scoped_lock(mutex_);
return filled_output_buffers_.empty() ? NULL : filled_output_buffers_.front();
}
void OpenMaxComponent::DropNextOutputBuffer() {
OMX_BUFFERHEADERTYPE* buffer = NULL;
{
ScopedLock scoped_lock(mutex_);
SB_DCHECK(!filled_output_buffers_.empty());
buffer = filled_output_buffers_.front();
filled_output_buffers_.pop();
}
buffer->nFilledLen = 0;
OMX_ERRORTYPE error = OMX_FillThisBuffer(handle_, buffer);
SB_DCHECK(error == OMX_ErrorNone);
}
void OpenMaxComponent::SendCommand(OMX_COMMANDTYPE command, int param) {
OMX_ERRORTYPE error = OMX_SendCommand(handle_, command, param, NULL);
SB_DCHECK(error == OMX_ErrorNone);
}
void OpenMaxComponent::WaitForCommandCompletion() {
for (;;) {
ScopedLock scoped_lock(mutex_);
for (EventDescriptions::iterator iter = event_descriptions_.begin();
iter != event_descriptions_.end(); ++iter) {
if (iter->event == OMX_EventCmdComplete) {
event_descriptions_.erase(iter);
return;
}
// Special case for OMX_CommandStateSet.
if (iter->event == OMX_EventError && iter->data1 == OMX_ErrorSameState) {
event_descriptions_.erase(iter);
return;
}
}
condition_variable_.Wait();
}
}
void OpenMaxComponent::SendCommandAndWaitForCompletion(OMX_COMMANDTYPE command,
int param) {
SendCommand(command, param);
WaitForCommandCompletion();
}
void OpenMaxComponent::EnableInputPortAndAllocateBuffers() {
SB_DCHECK(input_buffers_.empty());
OMXParamPortDefinition port_definition;
GetInputPortParam(&port_definition);
if (OnEnableInputPort(&port_definition)) {
SetPortParam(port_definition);
}
SendCommand(OMX_CommandPortEnable, input_port_);
for (int i = 0; i < port_definition.nBufferCountActual; ++i) {
OMX_BUFFERHEADERTYPE* buffer;
OMX_ERRORTYPE error = OMX_AllocateBuffer(handle_, &buffer, input_port_,
NULL, port_definition.nBufferSize);
SB_DCHECK(error == OMX_ErrorNone);
input_buffers_.push_back(buffer);
unused_input_buffers_.push(buffer);
}
WaitForCommandCompletion();
}
void OpenMaxComponent::EnableOutputPortAndAllocateBuffer() {
SB_DCHECK(!output_port_enabled_);
GetOutputPortParam(&output_port_definition_);
if (OnEnableOutputPort(&output_port_definition_)) {
SetPortParam(output_port_definition_);
}
SendCommand(OMX_CommandPortEnable, output_port_);
output_buffers_.reserve(output_port_definition_.nBufferCountActual);
for (int i = 0; i < output_port_definition_.nBufferCountActual; ++i) {
OMX_BUFFERHEADERTYPE* buffer;
OMX_ERRORTYPE error =
OMX_AllocateBuffer(handle_, &buffer, output_port_, NULL,
output_port_definition_.nBufferSize);
SB_DCHECK(error == OMX_ErrorNone);
output_buffers_.push_back(buffer);
}
WaitForCommandCompletion();
output_port_enabled_ = true;
for (size_t i = 0; i < output_buffers_.size(); ++i) {
output_buffers_[i]->nFilledLen = 0;
OMX_ERRORTYPE error = OMX_FillThisBuffer(handle_, output_buffers_[i]);
SB_DCHECK(error == OMX_ErrorNone);
}
}
OMX_BUFFERHEADERTYPE* OpenMaxComponent::GetUnusedInputBuffer() {
for (;;) {
{
ScopedLock scoped_lock(mutex_);
if (!unused_input_buffers_.empty()) {
OMX_BUFFERHEADERTYPE* buffer_header = unused_input_buffers_.front();
unused_input_buffers_.pop();
return buffer_header;
}
}
SbThreadSleep(kSbTimeMillisecond);
}
SB_NOTREACHED();
return NULL;
}
OMX_ERRORTYPE OpenMaxComponent::OnEvent(OMX_EVENTTYPE event,
OMX_U32 data1,
OMX_U32 data2,
OMX_PTR event_data) {
if (event == OMX_EventError && data1 != OMX_ErrorSameState) {
SB_NOTREACHED() << "OMX_EventError received with " << std::hex << data1
<< " " << data2;
return OMX_ErrorNone;
}
ScopedLock scoped_lock(mutex_);
if (event == OMX_EventPortSettingsChanged && data1 == output_port_) {
output_setting_changed_ = true;
return OMX_ErrorNone;
}
EventDescription event_desc;
event_desc.event = event;
event_desc.data1 = data1;
event_desc.data2 = data2;
event_desc.event_data = event_data;
event_descriptions_.push_back(event_desc);
condition_variable_.Signal();
return OMX_ErrorNone;
}
OMX_ERRORTYPE OpenMaxComponent::OnEmptyBufferDone(
OMX_BUFFERHEADERTYPE* buffer) {
ScopedLock scoped_lock(mutex_);
unused_input_buffers_.push(buffer);
}
void OpenMaxComponent::OnFillBufferDone(OMX_BUFFERHEADERTYPE* buffer) {
ScopedLock scoped_lock(mutex_);
filled_output_buffers_.push(buffer);
}
// static
OMX_ERRORTYPE OpenMaxComponent::EventHandler(OMX_HANDLETYPE handle,
OMX_PTR app_data,
OMX_EVENTTYPE event,
OMX_U32 data1,
OMX_U32 data2,
OMX_PTR event_data) {
SB_DCHECK(app_data != NULL);
OpenMaxComponent* component = reinterpret_cast<OpenMaxComponent*>(app_data);
SB_DCHECK(handle == component->handle_);
return component->OnEvent(event, data1, data2, event_data);
}
// static
OMX_ERRORTYPE OpenMaxComponent::EmptyBufferDone(OMX_HANDLETYPE handle,
OMX_PTR app_data,
OMX_BUFFERHEADERTYPE* buffer) {
SB_DCHECK(app_data != NULL);
OpenMaxComponent* component = reinterpret_cast<OpenMaxComponent*>(app_data);
SB_DCHECK(handle == component->handle_);
return component->OnEmptyBufferDone(buffer);
}
// static
OMX_ERRORTYPE OpenMaxComponent::FillBufferDone(OMX_HANDLETYPE handle,
OMX_PTR app_data,
OMX_BUFFERHEADERTYPE* buffer) {
SB_DCHECK(app_data != NULL);
OpenMaxComponent* component = reinterpret_cast<OpenMaxComponent*>(app_data);
SB_DCHECK(handle == component->handle_);
component->OnFillBufferDone(buffer);
return OMX_ErrorNone;
}
} // namespace open_max
} // namespace shared
} // namespace raspi
} // namespace starboard