diff options
author | Andreas Huber <andih@google.com> | 2010-12-15 15:17:42 -0800 |
---|---|---|
committer | Andreas Huber <andih@google.com> | 2010-12-15 15:18:26 -0800 |
commit | f933441648ef6a71dee783d733aac17b9508b452 (patch) | |
tree | 240f8068edb362cbea579659a963bbb029a2bac0 /media/libstagefright/ACodec.cpp | |
parent | 60c5b57edd3c8f4bdf6b38cf5b8a193ba770bb72 (diff) | |
download | frameworks_av-f933441648ef6a71dee783d733aac17b9508b452.zip frameworks_av-f933441648ef6a71dee783d733aac17b9508b452.tar.gz frameworks_av-f933441648ef6a71dee783d733aac17b9508b452.tar.bz2 |
Initial support for a true streaming player for mpeg2 transport streams.
Change-Id: I153eec439d260a5524b21270e16d36940ec3161a
Diffstat (limited to 'media/libstagefright/ACodec.cpp')
-rw-r--r-- | media/libstagefright/ACodec.cpp | 2097 |
1 files changed, 2097 insertions, 0 deletions
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp new file mode 100644 index 0000000..77276ab --- /dev/null +++ b/media/libstagefright/ACodec.cpp @@ -0,0 +1,2097 @@ +//#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 + |