blob: 8556b32659aa6427a0f26017dbc4135553157a4f [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. 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_image_decode_component.h"
#include <algorithm>
#include "starboard/configuration.h"
#include "starboard/memory.h"
#include "starboard/raspi/shared/open_max/decode_target_create.h"
#include "starboard/raspi/shared/open_max/decode_target_internal.h"
#include "starboard/thread.h"
namespace starboard {
namespace raspi {
namespace shared {
namespace open_max {
namespace {
struct MimeToFormat {
const char* mime_type;
OMX_IMAGE_CODINGTYPE format;
};
const MimeToFormat kSupportedFormats[] = {
{"image/jpeg", OMX_IMAGE_CodingJPEG},
// The image decode component doesn't handle PNG very well.
// { "image/png", OMX_IMAGE_CodingPNG },
// GIF decoding should work, but it has not been tested.
// { "image/gif", OMX_IMAGE_CodingGIF },
};
} // namespace
OpenMaxImageDecodeComponent::OpenMaxImageDecodeComponent()
: OpenMaxComponent("OMX.broadcom.image_decode"),
state_(kStateIdle),
graphics_context_provider_(NULL),
target_(NULL),
input_format_(OMX_IMAGE_CodingMax) {}
// static
OMX_IMAGE_CODINGTYPE OpenMaxImageDecodeComponent::GetCompressionFormat(
const char* mime_type) {
for (int index = 0; index < SB_ARRAY_SIZE_INT(kSupportedFormats); ++index) {
if (strcmp(mime_type, kSupportedFormats[index].mime_type) == 0) {
return kSupportedFormats[index].format;
}
}
return OMX_IMAGE_CodingMax;
}
SbDecodeTarget OpenMaxImageDecodeComponent::Decode(
SbDecodeTargetGraphicsContextProvider* provider,
const char* mime_type,
SbDecodeTargetFormat output_format,
const void* data,
int data_size) {
SB_DCHECK(target_ == NULL);
graphics_context_provider_ = provider;
SetInputFormat(mime_type, output_format);
// Start the component and enable the input port.
Start();
state_ = kStateInputReady;
// Process the encoded data. This will call OnEnableOutputPort() when the
// component is ready to start filling the output.
int data_size_written = 0;
for (;;) {
int write_size = data_size - data_size_written;
if (write_size > 0) {
write_size =
WriteData(reinterpret_cast<const uint8_t*>(data) + data_size_written,
write_size, kDataEOS, SbTimeGetMonotonicNow());
SB_DCHECK(write_size >= 0);
data_size_written += write_size;
}
int output_size = ProcessOutput();
if (state_ == kStateDecodeDone) {
break;
} else if (write_size == 0 && output_size == 0) {
// Wait for buffers to become available.
SbThreadSleep(kSbTimeMillisecond);
}
}
// Clean up.
state_ = kStateIdle;
CloseTunnel();
return target_;
}
void OpenMaxImageDecodeComponent::SetInputFormat(const char* mime_type,
SbDecodeTargetFormat format) {
OMXParam<OMX_IMAGE_PARAM_PORTFORMATTYPE, OMX_IndexParamImagePortFormat>
port_format;
GetInputPortParam(&port_format);
input_format_ = GetCompressionFormat(mime_type);
port_format.eCompressionFormat = input_format_;
SB_DCHECK(port_format.eCompressionFormat != OMX_IMAGE_CodingMax);
SetPortParam(port_format);
}
void OpenMaxImageDecodeComponent::SetOutputFormat(OMX_COLOR_FORMATTYPE format,
int width,
int height) {
target_ = DecodeTargetCreate(graphics_context_provider_,
kSbDecodeTargetFormat1PlaneRGBA, width, height);
target_->info.is_opaque =
(input_format_ == OMX_IMAGE_CodingPNG) ? false : true;
render_component_.SetOutputImage(target_->images[0]);
}
int OpenMaxImageDecodeComponent::ProcessOutput() {
OMX_BUFFERHEADERTYPE* buffer = GetOutputBuffer();
int output_size = 0;
if (buffer) {
SB_DCHECK(state_ == kStateOutputReady);
bool is_end_of_stream = (buffer->nFlags & OMX_BUFFERFLAG_EOS) != 0;
output_size = 1; // Signal to caller that some data was processed.
DropOutputBuffer(buffer);
if (is_end_of_stream) {
state_ = kStateDecodeDone;
}
}
return output_size;
}
bool OpenMaxImageDecodeComponent::OnEnableOutputPort(
OMXParamPortDefinition* port_definition) {
if (port_definition->nPortIndex == output_port_) {
// Our output port has been enabled. Tunnel to the egl render
// component to decode into a texture.
SB_DCHECK(state_ == kStateInputReady);
render_component_.ForwardPortCallbacks(this);
SetOutputComponent(&render_component_);
state_ = kStateSetTunnelOutput;
return false;
} else if (state_ == kStateSetTunnelOutput) {
// Tunnelled component's output port was just enabled.
state_ = kStateOutputReady;
return false;
} else {
// Got final settings for the tunnelled component's output port.
SB_DCHECK(state_ == kStateOutputReady);
SetOutputFormat(port_definition->format.video.eColorFormat,
port_definition->format.video.nFrameWidth,
port_definition->format.video.nFrameHeight);
return false;
}
}
} // namespace open_max
} // namespace shared
} // namespace raspi
} // namespace starboard