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