blob: 77276ab90da090d3618f9ad3f5021816f0f71362 [file] [log] [blame]
//#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/OMXClient.h>
#include <surfaceflinger/Surface.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();
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(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) {
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 0
err = mOMX->allocateBufferWithBackup(mNode, portIndex, mem, &buffer);
#else
err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
#endif
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;
}
// Increase the buffer count by one to allow for the ANativeWindow to hold
// on to one of the buffers.
def.nBufferCountActual++;
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
// Set up the native window.
// XXX TODO: Get the gralloc usage flags from the OMX plugin!
err = native_window_set_usage(
mNativeWindow.get(),
GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
if (err != 0) {
LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -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;
}
// XXX TODO: Do something so the ANativeWindow knows that we'll need to get
// the same set of buffers.
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
OMX_U32 i;
for (i = 0; i < def.nBufferCountActual; i++) {
android_native_buffer_t *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));
IOMX::buffer_id bufferId;
err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
&bufferId);
if (err != 0) {
break;
}
LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
mComponentName.c_str(),
bufferId, graphicBuffer.get());
BufferInfo info;
info.mBufferID = bufferId;
info.mStatus = BufferInfo::OWNED_BY_US;
info.mData = new ABuffer(0);
info.mGraphicBuffer = graphicBuffer;
mBuffers[kPortIndexOutput].push(info);
}
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 = i;
} else {
// Return the last two buffers to the native window.
// XXX TODO: The number of buffers the native window owns should
// probably be queried from it when we put the native window in
// fixed buffer pool mode (which needs to be implemented).
// Currently it's hard-coded to 2.
cancelStart = def.nBufferCountActual - 2;
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() {
android_native_buffer_t *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::freeOutputBuffersOwnedByNativeWindow() {
for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
BufferInfo *info =
&mBuffers[kPortIndexOutput].editItemAt(i);
if (info->mStatus ==
BufferInfo::OWNED_BY_NATIVE_WINDOW) {
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);
static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
|| format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
|| format.eColorFormat == OMX_COLOR_FormatCbYCrY
|| 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 {
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++);
}
}
////////////////////////////////////////////////////////////////////////////////
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));
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) {
LOGI("[%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) {
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));
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 (!(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());
}
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());
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) {
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)) {
CHECK_EQ(mCodec->mOMX->fillBuffer(
mCodec->mNode, info->mBufferID),
(status_t)OK);
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
}
} else {
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();
}
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));
AString componentName;
if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
componentName = "OMX.Nvidia.h264.decode";
} else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
componentName = "OMX.Nvidia.aac.decoder";
} else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) {
componentName = "OMX.Nvidia.mp3.decoder";
} else {
TRESPASS();
}
sp<CodecObserver> observer = new CodecObserver;
IOMX::node_id node;
CHECK_EQ(omx->allocateNode(componentName.c_str(), observer, &node),
(status_t)OK);
sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
observer->setNotificationMessage(notify);
mCodec->mComponentName = componentName;
mCodec->mOMX = omx;
mCodec->mNode = node;
mCodec->configureCodec(mime.c_str(), msg);
sp<RefBase> obj;
if (msg->findObject("surface", &obj)) {
mCodec->mNativeWindow = static_cast<Surface *>(obj.get());
}
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() {
LOGI("[%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() {
LOGI("[%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) {
}
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);
}
CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
(status_t)OK);
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
}
}
void ACodec::ExecutingState::resume() {
submitOutputBuffers();
// Post the first input buffer.
CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u);
BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0);
postFillThisBuffer(info);
}
void ACodec::ExecutingState::stateEntered() {
LOGI("[%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:
{
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
(status_t)OK);
mCodec->changeState(mCodec->mExecutingToIdleState);
handled = true;
break;
}
case kWhatFlush:
{
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 == OMX_IndexParamPortDefinition) {
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode,
OMX_CommandPortDisable, kPortIndexOutput),
(status_t)OK);
if (mCodec->mNativeWindow != NULL) {
CHECK_EQ((status_t)OK,
mCodec->freeOutputBuffersOwnedByNativeWindow());
}
mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
} 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:
{
mCodec->deferMessage(msg);
handled = true;
break;
}
default:
handled = BaseState::onMessageReceived(msg);
break;
}
return handled;
}
void ACodec::OutputPortSettingsChangedState::stateEntered() {
LOGI("[%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);
LOGV("[%s] Output port now reenabled.",
mCodec->mComponentName.c_str());
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() {
LOGI("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
}
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;
}
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() {
LOGI("[%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);
LOGI("[%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() {
LOGI("[%s] Now in ErrorState", mCodec->mComponentName.c_str());
}
bool ACodec::ErrorState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
LOGI("EVENT(%d, 0x%08lx, 0x%08lx)", event, data1, data2);
return true;
}
////////////////////////////////////////////////////////////////////////////////
ACodec::FlushingState::FlushingState(ACodec *codec)
: BaseState(codec) {
}
void ACodec::FlushingState::stateEntered() {
LOGI("[%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;
} else {
CHECK_EQ(data2, OMX_ALL);
CHECK(mFlushComplete[kPortIndexInput]);
CHECK(mFlushComplete[kPortIndexOutput]);
changeStateIfWeOwnAllBuffers();
}
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