blob: d4d07b2815d92922430f06a81e45413602f3d84f [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* 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.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "ACodec"
#include <media/stagefright/ACodec.h>
#include <binder/MemoryDealer.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/NativeWindowWrapper.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
#include <surfaceflinger/Surface.h>
#include <gui/SurfaceTextureClient.h>
#include <OMX_Component.h>
namespace android {
template<class T>
static void InitOMXParams(T *params) {
params->nSize = sizeof(T);
params->nVersion.s.nVersionMajor = 1;
params->nVersion.s.nVersionMinor = 0;
params->nVersion.s.nRevision = 0;
params->nVersion.s.nStep = 0;
}
struct CodecObserver : public BnOMXObserver {
CodecObserver() {}
void setNotificationMessage(const sp<AMessage> &msg) {
mNotify = msg;
}
// from IOMXObserver
virtual void onMessage(const omx_message &omx_msg) {
sp<AMessage> msg = mNotify->dup();
msg->setInt32("type", omx_msg.type);
msg->setPointer("node", omx_msg.node);
switch (omx_msg.type) {
case omx_message::EVENT:
{
msg->setInt32("event", omx_msg.u.event_data.event);
msg->setInt32("data1", omx_msg.u.event_data.data1);
msg->setInt32("data2", omx_msg.u.event_data.data2);
break;
}
case omx_message::EMPTY_BUFFER_DONE:
{
msg->setPointer("buffer", omx_msg.u.buffer_data.buffer);
break;
}
case omx_message::FILL_BUFFER_DONE:
{
msg->setPointer(
"buffer", omx_msg.u.extended_buffer_data.buffer);
msg->setInt32(
"range_offset",
omx_msg.u.extended_buffer_data.range_offset);
msg->setInt32(
"range_length",
omx_msg.u.extended_buffer_data.range_length);
msg->setInt32(
"flags",
omx_msg.u.extended_buffer_data.flags);
msg->setInt64(
"timestamp",
omx_msg.u.extended_buffer_data.timestamp);
msg->setPointer(
"platform_private",
omx_msg.u.extended_buffer_data.platform_private);
msg->setPointer(
"data_ptr",
omx_msg.u.extended_buffer_data.data_ptr);
break;
}
default:
TRESPASS();
break;
}
msg->post();
}
protected:
virtual ~CodecObserver() {}
private:
sp<AMessage> mNotify;
DISALLOW_EVIL_CONSTRUCTORS(CodecObserver);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::BaseState : public AState {
BaseState(ACodec *codec, const sp<AState> &parentState = NULL);
protected:
enum PortMode {
KEEP_BUFFERS,
RESUBMIT_BUFFERS,
FREE_BUFFERS,
};
ACodec *mCodec;
virtual PortMode getPortMode(OMX_U32 portIndex);
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
virtual void onOutputBufferDrained(const sp<AMessage> &msg);
virtual void onInputBufferFilled(const sp<AMessage> &msg);
void postFillThisBuffer(BufferInfo *info);
private:
bool onOMXMessage(const sp<AMessage> &msg);
bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID);
bool onOMXFillBufferDone(
IOMX::buffer_id bufferID,
size_t rangeOffset, size_t rangeLength,
OMX_U32 flags,
int64_t timeUs,
void *platformPrivate,
void *dataPtr);
void getMoreInputDataIfPossible();
DISALLOW_EVIL_CONSTRUCTORS(BaseState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::UninitializedState : public ACodec::BaseState {
UninitializedState(ACodec *codec);
protected:
virtual bool onMessageReceived(const sp<AMessage> &msg);
private:
void onSetup(const sp<AMessage> &msg);
DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::LoadedToIdleState : public ACodec::BaseState {
LoadedToIdleState(ACodec *codec);
protected:
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
virtual void stateEntered();
private:
status_t allocateBuffers();
DISALLOW_EVIL_CONSTRUCTORS(LoadedToIdleState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::IdleToExecutingState : public ACodec::BaseState {
IdleToExecutingState(ACodec *codec);
protected:
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
virtual void stateEntered();
private:
DISALLOW_EVIL_CONSTRUCTORS(IdleToExecutingState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::ExecutingState : public ACodec::BaseState {
ExecutingState(ACodec *codec);
void submitOutputBuffers();
// Submit output buffers to the decoder, submit input buffers to client
// to fill with data.
void resume();
// Returns true iff input and output buffers are in play.
bool active() const { return mActive; }
protected:
virtual PortMode getPortMode(OMX_U32 portIndex);
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual void stateEntered();
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
private:
bool mActive;
DISALLOW_EVIL_CONSTRUCTORS(ExecutingState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::OutputPortSettingsChangedState : public ACodec::BaseState {
OutputPortSettingsChangedState(ACodec *codec);
protected:
virtual PortMode getPortMode(OMX_U32 portIndex);
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual void stateEntered();
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
private:
DISALLOW_EVIL_CONSTRUCTORS(OutputPortSettingsChangedState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::ExecutingToIdleState : public ACodec::BaseState {
ExecutingToIdleState(ACodec *codec);
protected:
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual void stateEntered();
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
virtual void onOutputBufferDrained(const sp<AMessage> &msg);
virtual void onInputBufferFilled(const sp<AMessage> &msg);
private:
void changeStateIfWeOwnAllBuffers();
DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::IdleToLoadedState : public ACodec::BaseState {
IdleToLoadedState(ACodec *codec);
protected:
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual void stateEntered();
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
private:
DISALLOW_EVIL_CONSTRUCTORS(IdleToLoadedState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::ErrorState : public ACodec::BaseState {
ErrorState(ACodec *codec);
protected:
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual void stateEntered();
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
private:
DISALLOW_EVIL_CONSTRUCTORS(ErrorState);
};
////////////////////////////////////////////////////////////////////////////////
struct ACodec::FlushingState : public ACodec::BaseState {
FlushingState(ACodec *codec);
protected:
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual void stateEntered();
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
virtual void onOutputBufferDrained(const sp<AMessage> &msg);
virtual void onInputBufferFilled(const sp<AMessage> &msg);
private:
bool mFlushComplete[2];
void changeStateIfWeOwnAllBuffers();
DISALLOW_EVIL_CONSTRUCTORS(FlushingState);
};
////////////////////////////////////////////////////////////////////////////////
ACodec::ACodec()
: mNode(NULL),
mSentFormat(false) {
mUninitializedState = new UninitializedState(this);
mLoadedToIdleState = new LoadedToIdleState(this);
mIdleToExecutingState = new IdleToExecutingState(this);
mExecutingState = new ExecutingState(this);
mOutputPortSettingsChangedState =
new OutputPortSettingsChangedState(this);
mExecutingToIdleState = new ExecutingToIdleState(this);
mIdleToLoadedState = new IdleToLoadedState(this);
mErrorState = new ErrorState(this);
mFlushingState = new FlushingState(this);
mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
changeState(mUninitializedState);
}
ACodec::~ACodec() {
}
void ACodec::setNotificationMessage(const sp<AMessage> &msg) {
mNotify = msg;
}
void ACodec::initiateSetup(const sp<AMessage> &msg) {
msg->setWhat(kWhatSetup);
msg->setTarget(id());
msg->post();
}
void ACodec::signalFlush() {
(new AMessage(kWhatFlush, id()))->post();
}
void ACodec::signalResume() {
(new AMessage(kWhatResume, id()))->post();
}
void ACodec::initiateShutdown() {
(new AMessage(kWhatShutdown, id()))->post();
}
status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
CHECK(mDealer[portIndex] == NULL);
CHECK(mBuffers[portIndex].isEmpty());
if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
return allocateOutputBuffersFromNativeWindow();
}
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
LOGV("[%s] Allocating %lu buffers of size %lu on %s port",
mComponentName.c_str(),
def.nBufferCountActual, def.nBufferSize,
portIndex == kPortIndexInput ? "input" : "output");
size_t totalSize = def.nBufferCountActual * def.nBufferSize;
mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");
for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
CHECK(mem.get() != NULL);
IOMX::buffer_id buffer;
if (!strcasecmp(
mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.DECODER")) {
if (portIndex == kPortIndexInput && i == 0) {
// Only log this warning once per allocation round.
LOGW("OMX.TI.DUCATI1.VIDEO.DECODER requires the use of "
"OMX_AllocateBuffer instead of the preferred "
"OMX_UseBuffer. Vendor must fix this.");
}
err = mOMX->allocateBufferWithBackup(
mNode, portIndex, mem, &buffer);
} else {
err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
}
if (err != OK) {
return err;
}
BufferInfo info;
info.mBufferID = buffer;
info.mStatus = BufferInfo::OWNED_BY_US;
info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
mBuffers[portIndex].push(info);
}
return OK;
}
status_t ACodec::allocateOutputBuffersFromNativeWindow() {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
err = native_window_set_buffers_geometry(
mNativeWindow.get(),
def.format.video.nFrameWidth,
def.format.video.nFrameHeight,
def.format.video.eColorFormat);
if (err != 0) {
LOGE("native_window_set_buffers_geometry failed: %s (%d)",
strerror(-err), -err);
return err;
}
// Set up the native window.
OMX_U32 usage = 0;
err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
if (err != 0) {
LOGW("querying usage flags from OMX IL component failed: %d", err);
// XXX: Currently this error is logged, but not fatal.
usage = 0;
}
err = native_window_set_usage(
mNativeWindow.get(),
usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
if (err != 0) {
LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
return err;
}
int minUndequeuedBufs = 0;
err = mNativeWindow->query(
mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
&minUndequeuedBufs);
if (err != 0) {
LOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
strerror(-err), -err);
return err;
}
// XXX: Is this the right logic to use? It's not clear to me what the OMX
// buffer counts refer to - how do they account for the renderer holding on
// to buffers?
if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) {
OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs;
def.nBufferCountActual = newBufferCount;
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
LOGE("[%s] setting nBufferCountActual to %lu failed: %d",
mComponentName.c_str(), newBufferCount, err);
return err;
}
}
err = native_window_set_buffer_count(
mNativeWindow.get(), def.nBufferCountActual);
if (err != 0) {
LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
-err);
return err;
}
LOGV("[%s] Allocating %lu buffers from a native window of size %lu on "
"output port",
mComponentName.c_str(), def.nBufferCountActual, def.nBufferSize);
// Dequeue buffers and send them to OMX
for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) {
ANativeWindowBuffer *buf;
err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
if (err != 0) {
LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
break;
}
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
BufferInfo info;
info.mStatus = BufferInfo::OWNED_BY_US;
info.mData = new ABuffer(0);
info.mGraphicBuffer = graphicBuffer;
mBuffers[kPortIndexOutput].push(info);
IOMX::buffer_id bufferId;
err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
&bufferId);
if (err != 0) {
LOGE("registering GraphicBuffer %lu with OMX IL component failed: "
"%d", i, err);
break;
}
mBuffers[kPortIndexOutput].editItemAt(i).mBufferID = bufferId;
LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
mComponentName.c_str(),
bufferId, graphicBuffer.get());
}
OMX_U32 cancelStart;
OMX_U32 cancelEnd;
if (err != 0) {
// If an error occurred while dequeuing we need to cancel any buffers
// that were dequeued.
cancelStart = 0;
cancelEnd = mBuffers[kPortIndexOutput].size();
} else {
// Return the last two buffers to the native window.
cancelStart = def.nBufferCountActual - minUndequeuedBufs;
cancelEnd = def.nBufferCountActual;
}
for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
cancelBufferToNativeWindow(info);
}
return err;
}
status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) {
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
LOGV("[%s] Calling cancelBuffer on buffer %p",
mComponentName.c_str(), info->mBufferID);
int err = mNativeWindow->cancelBuffer(
mNativeWindow.get(), info->mGraphicBuffer.get());
CHECK_EQ(err, 0);
info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
return OK;
}
ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
ANativeWindowBuffer *buf;
CHECK_EQ(mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf), 0);
for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
BufferInfo *info =
&mBuffers[kPortIndexOutput].editItemAt(i);
if (info->mGraphicBuffer->handle == buf->handle) {
CHECK_EQ((int)info->mStatus,
(int)BufferInfo::OWNED_BY_NATIVE_WINDOW);
info->mStatus = BufferInfo::OWNED_BY_US;
return info;
}
}
TRESPASS();
return NULL;
}
status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) {
for (size_t i = mBuffers[portIndex].size(); i-- > 0;) {
CHECK_EQ((status_t)OK, freeBuffer(portIndex, i));
}
mDealer[portIndex].clear();
return OK;
}
status_t ACodec::freeOutputBuffersNotOwnedByComponent() {
for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
BufferInfo *info =
&mBuffers[kPortIndexOutput].editItemAt(i);
if (info->mStatus !=
BufferInfo::OWNED_BY_COMPONENT) {
// We shouldn't have sent out any buffers to the client at this
// point.
CHECK_NE((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM);
CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i));
}
}
return OK;
}
status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
CHECK(info->mStatus == BufferInfo::OWNED_BY_US
|| info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW);
if (portIndex == kPortIndexOutput && mNativeWindow != NULL
&& info->mStatus == BufferInfo::OWNED_BY_US) {
CHECK_EQ((status_t)OK, cancelBufferToNativeWindow(info));
}
CHECK_EQ(mOMX->freeBuffer(
mNode, portIndex, info->mBufferID),
(status_t)OK);
mBuffers[portIndex].removeAt(i);
return OK;
}
ACodec::BufferInfo *ACodec::findBufferByID(
uint32_t portIndex, IOMX::buffer_id bufferID,
ssize_t *index) {
for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
if (info->mBufferID == bufferID) {
if (index != NULL) {
*index = i;
}
return info;
}
}
TRESPASS();
return NULL;
}
void ACodec::setComponentRole(
bool isEncoder, const char *mime) {
struct MimeToRole {
const char *mime;
const char *decoderRole;
const char *encoderRole;
};
static const MimeToRole kMimeToRole[] = {
{ MEDIA_MIMETYPE_AUDIO_MPEG,
"audio_decoder.mp3", "audio_encoder.mp3" },
{ MEDIA_MIMETYPE_AUDIO_AMR_NB,
"audio_decoder.amrnb", "audio_encoder.amrnb" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB,
"audio_decoder.amrwb", "audio_encoder.amrwb" },
{ MEDIA_MIMETYPE_AUDIO_AAC,
"audio_decoder.aac", "audio_encoder.aac" },
{ MEDIA_MIMETYPE_VIDEO_AVC,
"video_decoder.avc", "video_encoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4,
"video_decoder.mpeg4", "video_encoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_H263,
"video_decoder.h263", "video_encoder.h263" },
};
static const size_t kNumMimeToRole =
sizeof(kMimeToRole) / sizeof(kMimeToRole[0]);
size_t i;
for (i = 0; i < kNumMimeToRole; ++i) {
if (!strcasecmp(mime, kMimeToRole[i].mime)) {
break;
}
}
if (i == kNumMimeToRole) {
return;
}
const char *role =
isEncoder ? kMimeToRole[i].encoderRole
: kMimeToRole[i].decoderRole;
if (role != NULL) {
OMX_PARAM_COMPONENTROLETYPE roleParams;
InitOMXParams(&roleParams);
strncpy((char *)roleParams.cRole,
role, OMX_MAX_STRINGNAME_SIZE - 1);
roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
status_t err = mOMX->setParameter(
mNode, OMX_IndexParamStandardComponentRole,
&roleParams, sizeof(roleParams));
if (err != OK) {
LOGW("[%s] Failed to set standard component role '%s'.",
mComponentName.c_str(), role);
}
}
}
void ACodec::configureCodec(
const char *mime, const sp<AMessage> &msg) {
setComponentRole(false /* isEncoder */, mime);
if (!strncasecmp(mime, "video/", 6)) {
int32_t width, height;
CHECK(msg->findInt32("width", &width));
CHECK(msg->findInt32("height", &height));
CHECK_EQ(setupVideoDecoder(mime, width, height),
(status_t)OK);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
int32_t numChannels, sampleRate;
CHECK(msg->findInt32("channel-count", &numChannels));
CHECK(msg->findInt32("sample-rate", &sampleRate));
CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
} else {
TRESPASS();
}
int32_t maxInputSize;
if (msg->findInt32("max-input-size", &maxInputSize)) {
CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize),
(status_t)OK);
} else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) {
CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192), // XXX
(status_t)OK);
}
}
status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
if (def.nBufferSize >= size) {
return OK;
}
def.nBufferSize = size;
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
CHECK(def.nBufferSize >= size);
return OK;
}
status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
OMX_AUDIO_PARAM_AACPROFILETYPE profile;
InitOMXParams(&profile);
profile.nPortIndex = kPortIndexInput;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
if (err != OK) {
return err;
}
profile.nChannels = numChannels;
profile.nSampleRate = sampleRate;
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
err = mOMX->setParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
return err;
}
status_t ACodec::setVideoPortFormatType(
OMX_U32 portIndex,
OMX_VIDEO_CODINGTYPE compressionFormat,
OMX_COLOR_FORMATTYPE colorFormat) {
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
InitOMXParams(&format);
format.nPortIndex = portIndex;
format.nIndex = 0;
bool found = false;
OMX_U32 index = 0;
for (;;) {
format.nIndex = index;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
if (err != OK) {
return err;
}
// The following assertion is violated by TI's video decoder.
// CHECK_EQ(format.nIndex, index);
if (!strcmp("OMX.TI.Video.encoder", mComponentName.c_str())) {
if (portIndex == kPortIndexInput
&& colorFormat == format.eColorFormat) {
// eCompressionFormat does not seem right.
found = true;
break;
}
if (portIndex == kPortIndexOutput
&& compressionFormat == format.eCompressionFormat) {
// eColorFormat does not seem right.
found = true;
break;
}
}
if (format.eCompressionFormat == compressionFormat
&& format.eColorFormat == colorFormat) {
found = true;
break;
}
++index;
}
if (!found) {
return UNKNOWN_ERROR;
}
status_t err = mOMX->setParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
return err;
}
status_t ACodec::setSupportedOutputFormat() {
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
InitOMXParams(&format);
format.nPortIndex = kPortIndexOutput;
format.nIndex = 0;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
CHECK_EQ(err, (status_t)OK);
CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
|| format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
|| format.eColorFormat == OMX_COLOR_FormatCbYCrY
|| format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
|| format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
return mOMX->setParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
}
status_t ACodec::setupVideoDecoder(
const char *mime, int32_t width, int32_t height) {
OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
compressionFormat = OMX_VIDEO_CodingAVC;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
compressionFormat = OMX_VIDEO_CodingMPEG4;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
compressionFormat = OMX_VIDEO_CodingH263;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) {
compressionFormat = OMX_VIDEO_CodingMPEG2;
} else {
TRESPASS();
}
status_t err = setVideoPortFormatType(
kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
if (err != OK) {
return err;
}
err = setSupportedOutputFormat();
if (err != OK) {
return err;
}
err = setVideoFormatOnPort(
kPortIndexInput, width, height, compressionFormat);
if (err != OK) {
return err;
}
err = setVideoFormatOnPort(
kPortIndexOutput, width, height, OMX_VIDEO_CodingUnused);
if (err != OK) {
return err;
}
return OK;
}
status_t ACodec::setVideoFormatOnPort(
OMX_U32 portIndex,
int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, (status_t)OK);
if (portIndex == kPortIndexInput) {
// XXX Need a (much) better heuristic to compute input buffer sizes.
const size_t X = 64 * 1024;
if (def.nBufferSize < X) {
def.nBufferSize = X;
}
}
CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
video_def->nFrameWidth = width;
video_def->nFrameHeight = height;
if (portIndex == kPortIndexInput) {
video_def->eCompressionFormat = compressionFormat;
video_def->eColorFormat = OMX_COLOR_FormatUnused;
}
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
return err;
}
status_t ACodec::initNativeWindow() {
if (mNativeWindow != NULL) {
return mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE);
}
mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_FALSE);
return OK;
}
bool ACodec::allYourBuffersAreBelongToUs(
OMX_U32 portIndex) {
for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
if (info->mStatus != BufferInfo::OWNED_BY_US
&& info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
LOGV("[%s] Buffer %p on port %ld still has status %d",
mComponentName.c_str(),
info->mBufferID, portIndex, info->mStatus);
return false;
}
}
return true;
}
bool ACodec::allYourBuffersAreBelongToUs() {
return allYourBuffersAreBelongToUs(kPortIndexInput)
&& allYourBuffersAreBelongToUs(kPortIndexOutput);
}
void ACodec::deferMessage(const sp<AMessage> &msg) {
bool wasEmptyBefore = mDeferredQueue.empty();
mDeferredQueue.push_back(msg);
}
void ACodec::processDeferredMessages() {
List<sp<AMessage> > queue = mDeferredQueue;
mDeferredQueue.clear();
List<sp<AMessage> >::iterator it = queue.begin();
while (it != queue.end()) {
onMessageReceived(*it++);
}
}
void ACodec::sendFormatChange() {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatOutputFormatChanged);
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
CHECK_EQ(mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)),
(status_t)OK);
CHECK_EQ((int)def.eDir, (int)OMX_DirOutput);
switch (def.eDomain) {
case OMX_PortDomainVideo:
{
OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video;
notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW);
notify->setInt32("width", videoDef->nFrameWidth);
notify->setInt32("height", videoDef->nFrameHeight);
OMX_CONFIG_RECTTYPE rect;
InitOMXParams(&rect);
rect.nPortIndex = kPortIndexOutput;
if (mOMX->getConfig(
mNode, OMX_IndexConfigCommonOutputCrop,
&rect, sizeof(rect)) != OK) {
rect.nLeft = 0;
rect.nTop = 0;
rect.nWidth = videoDef->nFrameWidth;
rect.nHeight = videoDef->nFrameHeight;
}
CHECK_GE(rect.nLeft, 0);
CHECK_GE(rect.nTop, 0);
CHECK_GE(rect.nWidth, 0u);
CHECK_GE(rect.nHeight, 0u);
CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth);
CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight);
notify->setRect(
"crop",
rect.nLeft,
rect.nTop,
rect.nLeft + rect.nWidth - 1,
rect.nTop + rect.nHeight - 1);
if (mNativeWindow != NULL) {
android_native_rect_t crop;
crop.left = rect.nLeft;
crop.top = rect.nTop;
crop.right = rect.nLeft + rect.nWidth;
crop.bottom = rect.nTop + rect.nHeight;
CHECK_EQ(0, native_window_set_crop(
mNativeWindow.get(), &crop));
}
break;
}
case OMX_PortDomainAudio:
{
OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
CHECK_EQ((int)audioDef->eEncoding, (int)OMX_AUDIO_CodingPCM);
OMX_AUDIO_PARAM_PCMMODETYPE params;
InitOMXParams(&params);
params.nPortIndex = kPortIndexOutput;
CHECK_EQ(mOMX->getParameter(
mNode, OMX_IndexParamAudioPcm,
&params, sizeof(params)),
(status_t)OK);
CHECK(params.nChannels == 1 || params.bInterleaved);
CHECK_EQ(params.nBitPerSample, 16u);
CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned);
CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear);
notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW);
notify->setInt32("channel-count", params.nChannels);
notify->setInt32("sample-rate", params.nSamplingRate);
break;
}
default:
TRESPASS();
}
notify->post();
mSentFormat = true;
}
////////////////////////////////////////////////////////////////////////////////
ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState)
: AState(parentState),
mCodec(codec) {
}
ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(OMX_U32 portIndex) {
return KEEP_BUFFERS;
}
bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatInputBufferFilled:
{
onInputBufferFilled(msg);
break;
}
case kWhatOutputBufferDrained:
{
onOutputBufferDrained(msg);
break;
}
case ACodec::kWhatOMXMessage:
{
return onOMXMessage(msg);
}
default:
return false;
}
return true;
}
bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) {
int32_t type;
CHECK(msg->findInt32("type", &type));
IOMX::node_id nodeID;
CHECK(msg->findPointer("node", &nodeID));
CHECK_EQ(nodeID, mCodec->mNode);
switch (type) {
case omx_message::EVENT:
{
int32_t event, data1, data2;
CHECK(msg->findInt32("event", &event));
CHECK(msg->findInt32("data1", &data1));
CHECK(msg->findInt32("data2", &data2));
if (event == OMX_EventCmdComplete
&& data1 == OMX_CommandFlush
&& data2 == (int32_t)OMX_ALL) {
// Use of this notification is not consistent across
// implementations. We'll drop this notification and rely
// on flush-complete notifications on the individual port
// indices instead.
return true;
}
return onOMXEvent(
static_cast<OMX_EVENTTYPE>(event),
static_cast<OMX_U32>(data1),
static_cast<OMX_U32>(data2));
}
case omx_message::EMPTY_BUFFER_DONE:
{
IOMX::buffer_id bufferID;
CHECK(msg->findPointer("buffer", &bufferID));
return onOMXEmptyBufferDone(bufferID);
}
case omx_message::FILL_BUFFER_DONE:
{
IOMX::buffer_id bufferID;
CHECK(msg->findPointer("buffer", &bufferID));
int32_t rangeOffset, rangeLength, flags;
int64_t timeUs;
void *platformPrivate;
void *dataPtr;
CHECK(msg->findInt32("range_offset", &rangeOffset));
CHECK(msg->findInt32("range_length", &rangeLength));
CHECK(msg->findInt32("flags", &flags));
CHECK(msg->findInt64("timestamp", &timeUs));
CHECK(msg->findPointer("platform_private", &platformPrivate));
CHECK(msg->findPointer("data_ptr", &dataPtr));
return onOMXFillBufferDone(
bufferID,
(size_t)rangeOffset, (size_t)rangeLength,
(OMX_U32)flags,
timeUs,
platformPrivate,
dataPtr);
}
default:
TRESPASS();
break;
}
}
bool ACodec::BaseState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
if (event != OMX_EventError) {
LOGV("[%s] EVENT(%d, 0x%08lx, 0x%08lx)",
mCodec->mComponentName.c_str(), event, data1, data2);
return false;
}
LOGE("[%s] ERROR(0x%08lx, 0x%08lx)",
mCodec->mComponentName.c_str(), data1, data2);
mCodec->changeState(mCodec->mErrorState);
return true;
}
bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
LOGV("[%s] onOMXEmptyBufferDone %p",
mCodec->mComponentName.c_str(), bufferID);
BufferInfo *info =
mCodec->findBufferByID(kPortIndexInput, bufferID);
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
info->mStatus = BufferInfo::OWNED_BY_US;
PortMode mode = getPortMode(kPortIndexInput);
switch (mode) {
case KEEP_BUFFERS:
break;
case RESUBMIT_BUFFERS:
postFillThisBuffer(info);
break;
default:
{
CHECK_EQ((int)mode, (int)FREE_BUFFERS);
TRESPASS(); // Not currently used
break;
}
}
return true;
}
void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
if (mCodec->mPortEOS[kPortIndexInput]) {
return;
}
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatFillThisBuffer);
notify->setPointer("buffer-id", info->mBufferID);
info->mData->meta()->clear();
notify->setObject("buffer", info->mData);
sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
reply->setPointer("buffer-id", info->mBufferID);
notify->setMessage("reply", reply);
notify->post();
info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
}
void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
IOMX::buffer_id bufferID;
CHECK(msg->findPointer("buffer-id", &bufferID));
sp<RefBase> obj;
int32_t err = OK;
if (!msg->findObject("buffer", &obj)) {
CHECK(msg->findInt32("err", &err));
LOGV("[%s] saw error %d instead of an input buffer",
mCodec->mComponentName.c_str(), err);
obj.clear();
}
sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID);
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_UPSTREAM);
info->mStatus = BufferInfo::OWNED_BY_US;
PortMode mode = getPortMode(kPortIndexInput);
switch (mode) {
case KEEP_BUFFERS:
{
if (buffer == NULL) {
mCodec->mPortEOS[kPortIndexInput] = true;
}
break;
}
case RESUBMIT_BUFFERS:
{
if (buffer != NULL) {
CHECK(!mCodec->mPortEOS[kPortIndexInput]);
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
int32_t isCSD;
if (buffer->meta()->findInt32("csd", &isCSD) && isCSD != 0) {
flags |= OMX_BUFFERFLAG_CODECCONFIG;
}
if (buffer != info->mData) {
if (0 && !(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
LOGV("[%s] Needs to copy input data.",
mCodec->mComponentName.c_str());
}
CHECK_LE(buffer->size(), info->mData->capacity());
memcpy(info->mData->data(), buffer->data(), buffer->size());
}
LOGV("[%s] calling emptyBuffer %p",
mCodec->mComponentName.c_str(), bufferID);
CHECK_EQ(mCodec->mOMX->emptyBuffer(
mCodec->mNode,
bufferID,
0,
buffer->size(),
flags,
timeUs),
(status_t)OK);
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
getMoreInputDataIfPossible();
} else if (!mCodec->mPortEOS[kPortIndexInput]) {
LOGV("[%s] Signalling EOS on the input port",
mCodec->mComponentName.c_str());
LOGV("[%s] calling emptyBuffer %p",
mCodec->mComponentName.c_str(), bufferID);
CHECK_EQ(mCodec->mOMX->emptyBuffer(
mCodec->mNode,
bufferID,
0,
0,
OMX_BUFFERFLAG_EOS,
0),
(status_t)OK);
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
mCodec->mPortEOS[kPortIndexInput] = true;
}
break;
default:
CHECK_EQ((int)mode, (int)FREE_BUFFERS);
break;
}
}
}
void ACodec::BaseState::getMoreInputDataIfPossible() {
if (mCodec->mPortEOS[kPortIndexInput]) {
return;
}
BufferInfo *eligible = NULL;
for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); ++i) {
BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);
#if 0
if (info->mStatus == BufferInfo::OWNED_BY_UPSTREAM) {
// There's already a "read" pending.
return;
}
#endif
if (info->mStatus == BufferInfo::OWNED_BY_US) {
eligible = info;
}
}
if (eligible == NULL) {
return;
}
postFillThisBuffer(eligible);
}
bool ACodec::BaseState::onOMXFillBufferDone(
IOMX::buffer_id bufferID,
size_t rangeOffset, size_t rangeLength,
OMX_U32 flags,
int64_t timeUs,
void *platformPrivate,
void *dataPtr) {
LOGV("[%s] onOMXFillBufferDone %p",
mCodec->mComponentName.c_str(), bufferID);
ssize_t index;
BufferInfo *info =
mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
info->mStatus = BufferInfo::OWNED_BY_US;
PortMode mode = getPortMode(kPortIndexOutput);
switch (mode) {
case KEEP_BUFFERS:
break;
case RESUBMIT_BUFFERS:
{
if (rangeLength == 0) {
if (!(flags & OMX_BUFFERFLAG_EOS)) {
LOGV("[%s] calling fillBuffer %p",
mCodec->mComponentName.c_str(), info->mBufferID);
CHECK_EQ(mCodec->mOMX->fillBuffer(
mCodec->mNode, info->mBufferID),
(status_t)OK);
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
}
} else {
if (!mCodec->mSentFormat) {
mCodec->sendFormatChange();
}
if (mCodec->mNativeWindow == NULL) {
info->mData->setRange(rangeOffset, rangeLength);
}
info->mData->meta()->setInt64("timeUs", timeUs);
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
notify->setPointer("buffer-id", info->mBufferID);
notify->setObject("buffer", info->mData);
sp<AMessage> reply =
new AMessage(kWhatOutputBufferDrained, mCodec->id());
reply->setPointer("buffer-id", info->mBufferID);
notify->setMessage("reply", reply);
notify->post();
info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
}
if (flags & OMX_BUFFERFLAG_EOS) {
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatEOS);
notify->post();
mCodec->mPortEOS[kPortIndexOutput] = true;
}
break;
}
default:
{
CHECK_EQ((int)mode, (int)FREE_BUFFERS);
CHECK_EQ((status_t)OK,
mCodec->freeBuffer(kPortIndexOutput, index));
break;
}
}
return true;
}
void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
IOMX::buffer_id bufferID;
CHECK(msg->findPointer("buffer-id", &bufferID));
ssize_t index;
BufferInfo *info =
mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM);
int32_t render;
if (mCodec->mNativeWindow != NULL
&& msg->findInt32("render", &render) && render != 0) {
// The client wants this buffer to be rendered.
CHECK_EQ(mCodec->mNativeWindow->queueBuffer(
mCodec->mNativeWindow.get(),
info->mGraphicBuffer.get()),
0);
info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
} else {
info->mStatus = BufferInfo::OWNED_BY_US;
}
PortMode mode = getPortMode(kPortIndexOutput);
switch (mode) {
case KEEP_BUFFERS:
{
// XXX fishy, revisit!!! What about the FREE_BUFFERS case below?
if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
// We cannot resubmit the buffer we just rendered, dequeue
// the spare instead.
info = mCodec->dequeueBufferFromNativeWindow();
}
break;
}
case RESUBMIT_BUFFERS:
{
if (!mCodec->mPortEOS[kPortIndexOutput]) {
if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
// We cannot resubmit the buffer we just rendered, dequeue
// the spare instead.
info = mCodec->dequeueBufferFromNativeWindow();
}
LOGV("[%s] calling fillBuffer %p",
mCodec->mComponentName.c_str(), info->mBufferID);
CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
(status_t)OK);
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
}
break;
}
default:
{
CHECK_EQ((int)mode, (int)FREE_BUFFERS);
CHECK_EQ((status_t)OK,
mCodec->freeBuffer(kPortIndexOutput, index));
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////
ACodec::UninitializedState::UninitializedState(ACodec *codec)
: BaseState(codec) {
}
bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
bool handled = false;
switch (msg->what()) {
case ACodec::kWhatSetup:
{
onSetup(msg);
handled = true;
break;
}
case ACodec::kWhatShutdown:
{
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatShutdownCompleted);
notify->post();
handled = true;
}
case ACodec::kWhatFlush:
{
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatFlushCompleted);
notify->post();
handled = true;
}
default:
return BaseState::onMessageReceived(msg);
}
return handled;
}
void ACodec::UninitializedState::onSetup(
const sp<AMessage> &msg) {
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
sp<IOMX> omx = client.interface();
AString mime;
CHECK(msg->findString("mime", &mime));
Vector<String8> matchingCodecs;
OMXCodec::findMatchingCodecs(
mime.c_str(),
false, // createEncoder
NULL, // matchComponentName
0, // flags
&matchingCodecs);
sp<CodecObserver> observer = new CodecObserver;
IOMX::node_id node = NULL;
AString componentName;
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).string();
status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
if (err == OK) {
break;
}
node = NULL;
}
CHECK(node != NULL);
sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
observer->setNotificationMessage(notify);
mCodec->mComponentName = componentName;
mCodec->mOMX = omx;
mCodec->mNode = node;
mCodec->mPortEOS[kPortIndexInput] =
mCodec->mPortEOS[kPortIndexOutput] = false;
mCodec->configureCodec(mime.c_str(), msg);
sp<RefBase> obj;
if (msg->findObject("native-window", &obj)
&& strncmp("OMX.google.", componentName.c_str(), 11)) {
sp<NativeWindowWrapper> nativeWindow(
static_cast<NativeWindowWrapper *>(obj.get()));
CHECK(nativeWindow != NULL);
mCodec->mNativeWindow = nativeWindow->getNativeWindow();
}
CHECK_EQ((status_t)OK, mCodec->initNativeWindow());
CHECK_EQ(omx->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle),
(status_t)OK);
mCodec->changeState(mCodec->mLoadedToIdleState);
}
////////////////////////////////////////////////////////////////////////////////
ACodec::LoadedToIdleState::LoadedToIdleState(ACodec *codec)
: BaseState(codec) {
}
void ACodec::LoadedToIdleState::stateEntered() {
LOGV("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());
CHECK_EQ(allocateBuffers(), (status_t)OK);
}
status_t ACodec::LoadedToIdleState::allocateBuffers() {
status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
if (err != OK) {
return err;
}
return mCodec->allocateBuffersOnPort(kPortIndexOutput);
}
bool ACodec::LoadedToIdleState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatShutdown:
{
mCodec->deferMessage(msg);
return true;
}
default:
return BaseState::onMessageReceived(msg);
}
}
bool ACodec::LoadedToIdleState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
switch (event) {
case OMX_EventCmdComplete:
{
CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandStateSet, OMX_StateExecuting),
(status_t)OK);
mCodec->changeState(mCodec->mIdleToExecutingState);
return true;
}
default:
return BaseState::onOMXEvent(event, data1, data2);
}
}
////////////////////////////////////////////////////////////////////////////////
ACodec::IdleToExecutingState::IdleToExecutingState(ACodec *codec)
: BaseState(codec) {
}
void ACodec::IdleToExecutingState::stateEntered() {
LOGV("[%s] Now Idle->Executing", mCodec->mComponentName.c_str());
}
bool ACodec::IdleToExecutingState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatShutdown:
{
mCodec->deferMessage(msg);
return true;
}
default:
return BaseState::onMessageReceived(msg);
}
}
bool ACodec::IdleToExecutingState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
switch (event) {
case OMX_EventCmdComplete:
{
CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
CHECK_EQ(data2, (OMX_U32)OMX_StateExecuting);
mCodec->mExecutingState->resume();
mCodec->changeState(mCodec->mExecutingState);
return true;
}
default:
return BaseState::onOMXEvent(event, data1, data2);
}
}
////////////////////////////////////////////////////////////////////////////////
ACodec::ExecutingState::ExecutingState(ACodec *codec)
: BaseState(codec),
mActive(false) {
}
ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode(
OMX_U32 portIndex) {
return RESUBMIT_BUFFERS;
}
void ACodec::ExecutingState::submitOutputBuffers() {
for (size_t i = 0; i < mCodec->mBuffers[kPortIndexOutput].size(); ++i) {
BufferInfo *info = &mCodec->mBuffers[kPortIndexOutput].editItemAt(i);
if (mCodec->mNativeWindow != NULL) {
CHECK(info->mStatus == BufferInfo::OWNED_BY_US
|| info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW);
if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
continue;
}
status_t err = mCodec->mNativeWindow->lockBuffer(
mCodec->mNativeWindow.get(),
info->mGraphicBuffer.get());
CHECK_EQ(err, (status_t)OK);
} else {
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
}
LOGV("[%s] calling fillBuffer %p",
mCodec->mComponentName.c_str(), info->mBufferID);
CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
(status_t)OK);
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
}
}
void ACodec::ExecutingState::resume() {
if (mActive) {
LOGV("[%s] We're already active, no need to resume.",
mCodec->mComponentName.c_str());
return;
}
submitOutputBuffers();
// Post the first input buffer.
CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u);
BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0);
postFillThisBuffer(info);
mActive = true;
}
void ACodec::ExecutingState::stateEntered() {
LOGV("[%s] Now Executing", mCodec->mComponentName.c_str());
mCodec->processDeferredMessages();
}
bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) {
bool handled = false;
switch (msg->what()) {
case kWhatShutdown:
{
mActive = false;
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
(status_t)OK);
mCodec->changeState(mCodec->mExecutingToIdleState);
handled = true;
break;
}
case kWhatFlush:
{
mActive = false;
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandFlush, OMX_ALL),
(status_t)OK);
mCodec->changeState(mCodec->mFlushingState);
handled = true;
break;
}
case kWhatResume:
{
resume();
handled = true;
break;
}
default:
handled = BaseState::onMessageReceived(msg);
break;
}
return handled;
}
bool ACodec::ExecutingState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
switch (event) {
case OMX_EventPortSettingsChanged:
{
CHECK_EQ(data1, (OMX_U32)kPortIndexOutput);
if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode,
OMX_CommandPortDisable, kPortIndexOutput),
(status_t)OK);
mCodec->freeOutputBuffersNotOwnedByComponent();
mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
} else if (data2 == OMX_IndexConfigCommonOutputCrop) {
mCodec->mSentFormat = false;
} else {
LOGV("[%s] OMX_EventPortSettingsChanged 0x%08lx",
mCodec->mComponentName.c_str(), data2);
}
return true;
}
case OMX_EventBufferFlag:
{
return true;
}
default:
return BaseState::onOMXEvent(event, data1, data2);
}
}
////////////////////////////////////////////////////////////////////////////////
ACodec::OutputPortSettingsChangedState::OutputPortSettingsChangedState(
ACodec *codec)
: BaseState(codec) {
}
ACodec::BaseState::PortMode ACodec::OutputPortSettingsChangedState::getPortMode(
OMX_U32 portIndex) {
if (portIndex == kPortIndexOutput) {
return FREE_BUFFERS;
}
CHECK_EQ(portIndex, (OMX_U32)kPortIndexInput);
return RESUBMIT_BUFFERS;
}
bool ACodec::OutputPortSettingsChangedState::onMessageReceived(
const sp<AMessage> &msg) {
bool handled = false;
switch (msg->what()) {
case kWhatFlush:
case kWhatShutdown:
case kWhatResume:
{
if (msg->what() == kWhatResume) {
LOGV("[%s] Deferring resume", mCodec->mComponentName.c_str());
}
mCodec->deferMessage(msg);
handled = true;
break;
}
default:
handled = BaseState::onMessageReceived(msg);
break;
}
return handled;
}
void ACodec::OutputPortSettingsChangedState::stateEntered() {
LOGV("[%s] Now handling output port settings change",
mCodec->mComponentName.c_str());
}
bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
switch (event) {
case OMX_EventCmdComplete:
{
if (data1 == (OMX_U32)OMX_CommandPortDisable) {
CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
LOGV("[%s] Output port now disabled.",
mCodec->mComponentName.c_str());
CHECK(mCodec->mBuffers[kPortIndexOutput].isEmpty());
mCodec->mDealer[kPortIndexOutput].clear();
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandPortEnable, kPortIndexOutput),
(status_t)OK);
CHECK_EQ(mCodec->allocateBuffersOnPort(kPortIndexOutput),
(status_t)OK);
return true;
} else if (data1 == (OMX_U32)OMX_CommandPortEnable) {
CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
mCodec->mSentFormat = false;
LOGV("[%s] Output port now reenabled.",
mCodec->mComponentName.c_str());
if (mCodec->mExecutingState->active()) {
mCodec->mExecutingState->submitOutputBuffers();
}
mCodec->changeState(mCodec->mExecutingState);
return true;
}
return false;
}
default:
return false;
}
}
////////////////////////////////////////////////////////////////////////////////
ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec)
: BaseState(codec) {
}
bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
bool handled = false;
switch (msg->what()) {
case kWhatFlush:
{
// Don't send me a flush request if you previously wanted me
// to shutdown.
TRESPASS();
break;
}
case kWhatShutdown:
{
// We're already doing that...
handled = true;
break;
}
default:
handled = BaseState::onMessageReceived(msg);
break;
}
return handled;
}
void ACodec::ExecutingToIdleState::stateEntered() {
LOGV("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
mCodec->mSentFormat = false;
}
bool ACodec::ExecutingToIdleState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
switch (event) {
case OMX_EventCmdComplete:
{
CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
changeStateIfWeOwnAllBuffers();
return true;
}
case OMX_EventPortSettingsChanged:
case OMX_EventBufferFlag:
{
// We're shutting down and don't care about this anymore.
return true;
}
default:
return BaseState::onOMXEvent(event, data1, data2);
}
}
void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
if (mCodec->allYourBuffersAreBelongToUs()) {
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandStateSet, OMX_StateLoaded),
(status_t)OK);
CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexInput), (status_t)OK);
CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexOutput), (status_t)OK);
mCodec->changeState(mCodec->mIdleToLoadedState);
}
}
void ACodec::ExecutingToIdleState::onInputBufferFilled(
const sp<AMessage> &msg) {
BaseState::onInputBufferFilled(msg);
changeStateIfWeOwnAllBuffers();
}
void ACodec::ExecutingToIdleState::onOutputBufferDrained(
const sp<AMessage> &msg) {
BaseState::onOutputBufferDrained(msg);
changeStateIfWeOwnAllBuffers();
}
////////////////////////////////////////////////////////////////////////////////
ACodec::IdleToLoadedState::IdleToLoadedState(ACodec *codec)
: BaseState(codec) {
}
bool ACodec::IdleToLoadedState::onMessageReceived(const sp<AMessage> &msg) {
bool handled = false;
switch (msg->what()) {
case kWhatShutdown:
{
// We're already doing that...
handled = true;
break;
}
case kWhatFlush:
{
// Don't send me a flush request if you previously wanted me
// to shutdown.
TRESPASS();
break;
}
default:
handled = BaseState::onMessageReceived(msg);
break;
}
return handled;
}
void ACodec::IdleToLoadedState::stateEntered() {
LOGV("[%s] Now Idle->Loaded", mCodec->mComponentName.c_str());
}
bool ACodec::IdleToLoadedState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
switch (event) {
case OMX_EventCmdComplete:
{
CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
CHECK_EQ(data2, (OMX_U32)OMX_StateLoaded);
LOGV("[%s] Now Loaded", mCodec->mComponentName.c_str());
CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
mCodec->mNativeWindow.clear();
mCodec->mNode = NULL;
mCodec->mOMX.clear();
mCodec->mComponentName.clear();
mCodec->changeState(mCodec->mUninitializedState);
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatShutdownCompleted);
notify->post();
return true;
}
default:
return BaseState::onOMXEvent(event, data1, data2);
}
}
////////////////////////////////////////////////////////////////////////////////
ACodec::ErrorState::ErrorState(ACodec *codec)
: BaseState(codec) {
}
bool ACodec::ErrorState::onMessageReceived(const sp<AMessage> &msg) {
return BaseState::onMessageReceived(msg);
}
void ACodec::ErrorState::stateEntered() {
LOGV("[%s] Now in ErrorState", mCodec->mComponentName.c_str());
}
bool ACodec::ErrorState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
LOGV("EVENT(%d, 0x%08lx, 0x%08lx)", event, data1, data2);
return true;
}
////////////////////////////////////////////////////////////////////////////////
ACodec::FlushingState::FlushingState(ACodec *codec)
: BaseState(codec) {
}
void ACodec::FlushingState::stateEntered() {
LOGV("[%s] Now Flushing", mCodec->mComponentName.c_str());
mFlushComplete[kPortIndexInput] = mFlushComplete[kPortIndexOutput] = false;
}
bool ACodec::FlushingState::onMessageReceived(const sp<AMessage> &msg) {
bool handled = false;
switch (msg->what()) {
case kWhatShutdown:
{
mCodec->deferMessage(msg);
break;
}
case kWhatFlush:
{
// We're already doing this right now.
handled = true;
break;
}
default:
handled = BaseState::onMessageReceived(msg);
break;
}
return handled;
}
bool ACodec::FlushingState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
switch (event) {
case OMX_EventCmdComplete:
{
CHECK_EQ(data1, (OMX_U32)OMX_CommandFlush);
if (data2 == kPortIndexInput || data2 == kPortIndexOutput) {
CHECK(!mFlushComplete[data2]);
mFlushComplete[data2] = true;
if (mFlushComplete[kPortIndexInput]
&& mFlushComplete[kPortIndexOutput]) {
changeStateIfWeOwnAllBuffers();
}
} else {
CHECK_EQ(data2, OMX_ALL);
CHECK(mFlushComplete[kPortIndexInput]);
CHECK(mFlushComplete[kPortIndexOutput]);
changeStateIfWeOwnAllBuffers();
}
return true;
}
case OMX_EventPortSettingsChanged:
{
sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec->id());
msg->setInt32("type", omx_message::EVENT);
msg->setPointer("node", mCodec->mNode);
msg->setInt32("event", event);
msg->setInt32("data1", data1);
msg->setInt32("data2", data2);
LOGV("[%s] Deferring OMX_EventPortSettingsChanged",
mCodec->mComponentName.c_str());
mCodec->deferMessage(msg);
return true;
}
default:
return BaseState::onOMXEvent(event, data1, data2);
}
return true;
}
void ACodec::FlushingState::onOutputBufferDrained(const sp<AMessage> &msg) {
BaseState::onOutputBufferDrained(msg);
changeStateIfWeOwnAllBuffers();
}
void ACodec::FlushingState::onInputBufferFilled(const sp<AMessage> &msg) {
BaseState::onInputBufferFilled(msg);
changeStateIfWeOwnAllBuffers();
}
void ACodec::FlushingState::changeStateIfWeOwnAllBuffers() {
if (mFlushComplete[kPortIndexInput]
&& mFlushComplete[kPortIndexOutput]
&& mCodec->allYourBuffersAreBelongToUs()) {
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatFlushCompleted);
notify->post();
mCodec->mPortEOS[kPortIndexInput] =
mCodec->mPortEOS[kPortIndexOutput] = false;
mCodec->changeState(mCodec->mExecutingState);
}
}
} // namespace android