/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "ACodec" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { template 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 &msg) { mNotify = msg; } // from IOMXObserver virtual void onMessage(const omx_message &omx_msg) { sp 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 mNotify; DISALLOW_EVIL_CONSTRUCTORS(CodecObserver); }; //////////////////////////////////////////////////////////////////////////////// struct ACodec::BaseState : public AState { BaseState(ACodec *codec, const sp &parentState = NULL); protected: enum PortMode { KEEP_BUFFERS, RESUBMIT_BUFFERS, FREE_BUFFERS, }; ACodec *mCodec; virtual PortMode getPortMode(OMX_U32 portIndex); virtual bool onMessageReceived(const sp &msg); virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2); virtual void onOutputBufferDrained(const sp &msg); virtual void onInputBufferFilled(const sp &msg); void postFillThisBuffer(BufferInfo *info); private: bool onOMXMessage(const sp &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 &msg); private: void onSetup(const sp &msg); DISALLOW_EVIL_CONSTRUCTORS(UninitializedState); }; //////////////////////////////////////////////////////////////////////////////// struct ACodec::LoadedToIdleState : public ACodec::BaseState { LoadedToIdleState(ACodec *codec); protected: virtual bool onMessageReceived(const sp &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 &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 &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 &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 &msg); virtual void stateEntered(); virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2); virtual void onOutputBufferDrained(const sp &msg); virtual void onInputBufferFilled(const sp &msg); private: void changeStateIfWeOwnAllBuffers(); DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState); }; //////////////////////////////////////////////////////////////////////////////// struct ACodec::IdleToLoadedState : public ACodec::BaseState { IdleToLoadedState(ACodec *codec); protected: virtual bool onMessageReceived(const sp &msg); virtual void stateEntered(); virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2); private: DISALLOW_EVIL_CONSTRUCTORS(IdleToLoadedState); }; //////////////////////////////////////////////////////////////////////////////// struct ACodec::FlushingState : public ACodec::BaseState { FlushingState(ACodec *codec); protected: virtual bool onMessageReceived(const sp &msg); virtual void stateEntered(); virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2); virtual void onOutputBufferDrained(const sp &msg); virtual void onInputBufferFilled(const sp &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); mFlushingState = new FlushingState(this); mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false; changeState(mUninitializedState); } ACodec::~ACodec() { } void ACodec::setNotificationMessage(const sp &msg) { mNotify = msg; } void ACodec::initiateSetup(const sp &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 mem = mDealer[portIndex]->allocate(def.nBufferSize); CHECK(mem.get() != NULL); IOMX::buffer_id buffer; if (!strcasecmp( mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.DECODER")) { if (portIndex == kPortIndexInput && i == 0) { // Only log this warning once per allocation round. LOGW("OMX.TI.DUCATI1.VIDEO.DECODER requires the use of " "OMX_AllocateBuffer instead of the preferred " "OMX_UseBuffer. Vendor must fix this."); } err = mOMX->allocateBufferWithBackup( mNode, portIndex, mem, &buffer); } else { err = mOMX->useBuffer(mNode, portIndex, mem, &buffer); } if (err != OK) { return err; } BufferInfo info; info.mBufferID = buffer; info.mStatus = BufferInfo::OWNED_BY_US; info.mData = new ABuffer(mem->pointer(), def.nBufferSize); mBuffers[portIndex].push(info); } return OK; } status_t ACodec::allocateOutputBuffersFromNativeWindow() { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); def.nPortIndex = kPortIndexOutput; status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); if (err != OK) { return err; } err = native_window_set_scaling_mode(mNativeWindow.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); if (err != OK) { return err; } err = native_window_set_buffers_geometry( mNativeWindow.get(), def.format.video.nFrameWidth, def.format.video.nFrameHeight, def.format.video.eColorFormat); if (err != 0) { LOGE("native_window_set_buffers_geometry failed: %s (%d)", strerror(-err), -err); return err; } // Set up the native window. OMX_U32 usage = 0; err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage); if (err != 0) { LOGW("querying usage flags from OMX IL component failed: %d", err); // XXX: Currently this error is logged, but not fatal. usage = 0; } err = native_window_set_usage( mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); if (err != 0) { LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err); return err; } int minUndequeuedBufs = 0; err = mNativeWindow->query( mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs); if (err != 0) { LOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)", strerror(-err), -err); return err; } // XXX: Is this the right logic to use? It's not clear to me what the OMX // buffer counts refer to - how do they account for the renderer holding on // to buffers? if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) { OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs; def.nBufferCountActual = newBufferCount; err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); if (err != OK) { LOGE("[%s] setting nBufferCountActual to %lu failed: %d", mComponentName.c_str(), newBufferCount, err); return err; } } err = native_window_set_buffer_count( mNativeWindow.get(), def.nBufferCountActual); if (err != 0) { LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err), -err); return err; } LOGV("[%s] Allocating %lu buffers from a native window of size %lu on " "output port", mComponentName.c_str(), def.nBufferCountActual, def.nBufferSize); // Dequeue buffers and send them to OMX for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) { ANativeWindowBuffer *buf; err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); if (err != 0) { LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; } sp graphicBuffer(new GraphicBuffer(buf, false)); BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; info.mData = new ABuffer(0); info.mGraphicBuffer = graphicBuffer; mBuffers[kPortIndexOutput].push(info); IOMX::buffer_id bufferId; err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer, &bufferId); if (err != 0) { LOGE("registering GraphicBuffer %lu with OMX IL component failed: " "%d", i, err); break; } mBuffers[kPortIndexOutput].editItemAt(i).mBufferID = bufferId; LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)", mComponentName.c_str(), bufferId, graphicBuffer.get()); } OMX_U32 cancelStart; OMX_U32 cancelEnd; if (err != 0) { // If an error occurred while dequeuing we need to cancel any buffers // that were dequeued. cancelStart = 0; cancelEnd = mBuffers[kPortIndexOutput].size(); } else { // Return the last two buffers to the native window. cancelStart = def.nBufferCountActual - minUndequeuedBufs; cancelEnd = def.nBufferCountActual; } for (OMX_U32 i = cancelStart; i < cancelEnd; i++) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); cancelBufferToNativeWindow(info); } return err; } status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US); LOGV("[%s] Calling cancelBuffer on buffer %p", mComponentName.c_str(), info->mBufferID); int err = mNativeWindow->cancelBuffer( mNativeWindow.get(), info->mGraphicBuffer.get()); CHECK_EQ(err, 0); info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; return OK; } ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { ANativeWindowBuffer *buf; if (mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf) != 0) { LOGE("dequeueBuffer failed."); return NULL; } 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 &msg) { setComponentRole(false /* isEncoder */, mime); if (!strncasecmp(mime, "video/", 6)) { int32_t width, height; CHECK(msg->findInt32("width", &width)); CHECK(msg->findInt32("height", &height)); CHECK_EQ(setupVideoDecoder(mime, width, height), (status_t)OK); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { int32_t numChannels, sampleRate; CHECK(msg->findInt32("channel-count", &numChannels)); CHECK(msg->findInt32("sample-rate", &sampleRate)); CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { } else { TRESPASS(); } int32_t maxInputSize; if (msg->findInt32("max-input-size", &maxInputSize)) { CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize), (status_t)OK); } else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) { CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192), // XXX (status_t)OK); } } status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); def.nPortIndex = portIndex; status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); if (err != OK) { return err; } if (def.nBufferSize >= size) { return OK; } def.nBufferSize = size; err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); if (err != OK) { return err; } err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); if (err != OK) { return err; } CHECK(def.nBufferSize >= size); return OK; } status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) { OMX_AUDIO_PARAM_AACPROFILETYPE profile; InitOMXParams(&profile); profile.nPortIndex = kPortIndexInput; status_t err = mOMX->getParameter( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); if (err != OK) { return err; } profile.nChannels = numChannels; profile.nSampleRate = sampleRate; profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; err = mOMX->setParameter( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); return err; } status_t ACodec::setVideoPortFormatType( OMX_U32 portIndex, OMX_VIDEO_CODINGTYPE compressionFormat, OMX_COLOR_FORMATTYPE colorFormat) { OMX_VIDEO_PARAM_PORTFORMATTYPE format; InitOMXParams(&format); format.nPortIndex = portIndex; format.nIndex = 0; bool found = false; OMX_U32 index = 0; for (;;) { format.nIndex = index; status_t err = mOMX->getParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); if (err != OK) { return err; } // The following assertion is violated by TI's video decoder. // CHECK_EQ(format.nIndex, index); if (!strcmp("OMX.TI.Video.encoder", mComponentName.c_str())) { if (portIndex == kPortIndexInput && colorFormat == format.eColorFormat) { // eCompressionFormat does not seem right. found = true; break; } if (portIndex == kPortIndexOutput && compressionFormat == format.eCompressionFormat) { // eColorFormat does not seem right. found = true; break; } } if (format.eCompressionFormat == compressionFormat && format.eColorFormat == colorFormat) { found = true; break; } ++index; } if (!found) { return UNKNOWN_ERROR; } status_t err = mOMX->setParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); return err; } status_t ACodec::setSupportedOutputFormat() { OMX_VIDEO_PARAM_PORTFORMATTYPE format; InitOMXParams(&format); format.nPortIndex = kPortIndexOutput; format.nIndex = 0; status_t err = mOMX->getParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); CHECK_EQ(err, (status_t)OK); CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused); CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar || format.eColorFormat == OMX_COLOR_FormatCbYCrY || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar); return mOMX->setParameter( mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); } status_t ACodec::setupVideoDecoder( const char *mime, int32_t width, int32_t height) { OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { compressionFormat = OMX_VIDEO_CodingAVC; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { compressionFormat = OMX_VIDEO_CodingMPEG4; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { compressionFormat = OMX_VIDEO_CodingH263; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) { compressionFormat = OMX_VIDEO_CodingMPEG2; } else { TRESPASS(); } status_t err = setVideoPortFormatType( kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused); if (err != OK) { return err; } err = setSupportedOutputFormat(); if (err != OK) { return err; } err = setVideoFormatOnPort( kPortIndexInput, width, height, compressionFormat); if (err != OK) { return err; } err = setVideoFormatOnPort( kPortIndexOutput, width, height, OMX_VIDEO_CodingUnused); if (err != OK) { return err; } return OK; } status_t ACodec::setVideoFormatOnPort( OMX_U32 portIndex, int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) { OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); def.nPortIndex = portIndex; OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, (status_t)OK); if (portIndex == kPortIndexInput) { // XXX Need a (much) better heuristic to compute input buffer sizes. const size_t X = 64 * 1024; if (def.nBufferSize < X) { def.nBufferSize = X; } } CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo); video_def->nFrameWidth = width; video_def->nFrameHeight = height; if (portIndex == kPortIndexInput) { video_def->eCompressionFormat = compressionFormat; video_def->eColorFormat = OMX_COLOR_FormatUnused; } err = mOMX->setParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); return err; } status_t ACodec::initNativeWindow() { if (mNativeWindow != NULL) { return mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE); } mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_FALSE); return OK; } bool ACodec::allYourBuffersAreBelongToUs( OMX_U32 portIndex) { for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) { BufferInfo *info = &mBuffers[portIndex].editItemAt(i); if (info->mStatus != BufferInfo::OWNED_BY_US && info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) { LOGV("[%s] Buffer %p on port %ld still has status %d", mComponentName.c_str(), info->mBufferID, portIndex, info->mStatus); return false; } } return true; } bool ACodec::allYourBuffersAreBelongToUs() { return allYourBuffersAreBelongToUs(kPortIndexInput) && allYourBuffersAreBelongToUs(kPortIndexOutput); } void ACodec::deferMessage(const sp &msg) { bool wasEmptyBefore = mDeferredQueue.empty(); mDeferredQueue.push_back(msg); } void ACodec::processDeferredMessages() { List > queue = mDeferredQueue; mDeferredQueue.clear(); List >::iterator it = queue.begin(); while (it != queue.end()) { onMessageReceived(*it++); } } void ACodec::sendFormatChange() { sp notify = mNotify->dup(); notify->setInt32("what", kWhatOutputFormatChanged); OMX_PARAM_PORTDEFINITIONTYPE def; InitOMXParams(&def); def.nPortIndex = kPortIndexOutput; CHECK_EQ(mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)), (status_t)OK); CHECK_EQ((int)def.eDir, (int)OMX_DirOutput); switch (def.eDomain) { case OMX_PortDomainVideo: { OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video; notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW); notify->setInt32("width", videoDef->nFrameWidth); notify->setInt32("height", videoDef->nFrameHeight); OMX_CONFIG_RECTTYPE rect; InitOMXParams(&rect); rect.nPortIndex = kPortIndexOutput; if (mOMX->getConfig( mNode, OMX_IndexConfigCommonOutputCrop, &rect, sizeof(rect)) != OK) { rect.nLeft = 0; rect.nTop = 0; rect.nWidth = videoDef->nFrameWidth; rect.nHeight = videoDef->nFrameHeight; } CHECK_GE(rect.nLeft, 0); CHECK_GE(rect.nTop, 0); CHECK_GE(rect.nWidth, 0u); CHECK_GE(rect.nHeight, 0u); CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth); CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight); notify->setRect( "crop", rect.nLeft, rect.nTop, rect.nLeft + rect.nWidth - 1, rect.nTop + rect.nHeight - 1); if (mNativeWindow != NULL) { android_native_rect_t crop; crop.left = rect.nLeft; crop.top = rect.nTop; crop.right = rect.nLeft + rect.nWidth; crop.bottom = rect.nTop + rect.nHeight; CHECK_EQ(0, native_window_set_crop( mNativeWindow.get(), &crop)); } break; } case OMX_PortDomainAudio: { OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio; CHECK_EQ((int)audioDef->eEncoding, (int)OMX_AUDIO_CodingPCM); OMX_AUDIO_PARAM_PCMMODETYPE params; InitOMXParams(¶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; } void ACodec::signalError(OMX_ERRORTYPE error) { sp notify = mNotify->dup(); notify->setInt32("what", ACodec::kWhatError); notify->setInt32("omx-error", error); notify->post(); } //////////////////////////////////////////////////////////////////////////////// ACodec::BaseState::BaseState(ACodec *codec, const sp &parentState) : AState(parentState), mCodec(codec) { } ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(OMX_U32 portIndex) { return KEEP_BUFFERS; } bool ACodec::BaseState::onMessageReceived(const sp &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 &msg) { int32_t type; CHECK(msg->findInt32("type", &type)); IOMX::node_id nodeID; CHECK(msg->findPointer("node", &nodeID)); CHECK_EQ(nodeID, mCodec->mNode); switch (type) { case omx_message::EVENT: { int32_t event, data1, data2; CHECK(msg->findInt32("event", &event)); CHECK(msg->findInt32("data1", &data1)); CHECK(msg->findInt32("data2", &data2)); if (event == OMX_EventCmdComplete && data1 == OMX_CommandFlush && data2 == (int32_t)OMX_ALL) { // Use of this notification is not consistent across // implementations. We'll drop this notification and rely // on flush-complete notifications on the individual port // indices instead. return true; } return onOMXEvent( static_cast(event), static_cast(data1), static_cast(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)", mCodec->mComponentName.c_str(), data1); mCodec->signalError((OMX_ERRORTYPE)data1); 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 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 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 &msg) { IOMX::buffer_id bufferID; CHECK(msg->findPointer("buffer-id", &bufferID)); sp 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 buffer = static_cast(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()); } if (flags & OMX_BUFFERFLAG_CODECCONFIG) { LOGV("[%s] calling emptyBuffer %p w/ codec specific data", mCodec->mComponentName.c_str(), bufferID); } else { LOGV("[%s] calling emptyBuffer %p w/ time %lld us", mCodec->mComponentName.c_str(), bufferID, timeUs); } 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 signalling EOS", 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 time %lld us", mCodec->mComponentName.c_str(), bufferID, timeUs); 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 notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatDrainThisBuffer); notify->setPointer("buffer-id", info->mBufferID); notify->setObject("buffer", info->mData); sp 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 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 &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. if (mCodec->mNativeWindow->queueBuffer( mCodec->mNativeWindow.get(), info->mGraphicBuffer.get()) == OK) { info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; } else { mCodec->signalError(); info->mStatus = BufferInfo::OWNED_BY_US; } } 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(); } if (info != NULL) { 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 &msg) { bool handled = false; switch (msg->what()) { case ACodec::kWhatSetup: { onSetup(msg); handled = true; break; } case ACodec::kWhatShutdown: { sp notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatShutdownCompleted); notify->post(); handled = true; break; } case ACodec::kWhatFlush: { sp notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatFlushCompleted); notify->post(); handled = true; break; } default: return BaseState::onMessageReceived(msg); } return handled; } void ACodec::UninitializedState::onSetup( const sp &msg) { OMXClient client; CHECK_EQ(client.connect(), (status_t)OK); sp omx = client.interface(); AString mime; CHECK(msg->findString("mime", &mime)); Vector matchingCodecs; OMXCodec::findMatchingCodecs( mime.c_str(), false, // createEncoder NULL, // matchComponentName 0, // flags &matchingCodecs); sp observer = new CodecObserver; IOMX::node_id node = NULL; AString componentName; for (size_t matchIndex = 0; matchIndex < matchingCodecs.size(); ++matchIndex) { componentName = matchingCodecs.itemAt(matchIndex).string(); pid_t tid = androidGetTid(); int prevPriority = androidGetThreadPriority(tid); androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND); status_t err = omx->allocateNode(componentName.c_str(), observer, &node); androidSetThreadPriority(tid, prevPriority); if (err == OK) { break; } node = NULL; } if (node == NULL) { LOGE("Unable to instantiate a decoder for type '%s'.", mime.c_str()); mCodec->signalError(OMX_ErrorComponentNotFound); return; } sp 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 obj; if (msg->findObject("native-window", &obj) && strncmp("OMX.google.", componentName.c_str(), 11)) { sp nativeWindow( static_cast(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()); status_t err; if ((err = allocateBuffers()) != OK) { LOGE("Failed to allocate buffers after transitioning to IDLE state " "(error 0x%08x)", err); mCodec->signalError(); } } 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 &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 &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 &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 &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); status_t err; if ((err = mCodec->allocateBuffersOnPort( kPortIndexOutput)) != OK) { LOGE("Failed to allocate output port buffers after " "port reconfiguration (error 0x%08x)", err); mCodec->signalError(); } 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 &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 &msg) { BaseState::onInputBufferFilled(msg); changeStateIfWeOwnAllBuffers(); } void ACodec::ExecutingToIdleState::onOutputBufferDrained( const sp &msg) { BaseState::onOutputBufferDrained(msg); changeStateIfWeOwnAllBuffers(); } //////////////////////////////////////////////////////////////////////////////// ACodec::IdleToLoadedState::IdleToLoadedState(ACodec *codec) : BaseState(codec) { } bool ACodec::IdleToLoadedState::onMessageReceived(const sp &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 notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatShutdownCompleted); notify->post(); return true; } default: return BaseState::onOMXEvent(event, data1, data2); } } //////////////////////////////////////////////////////////////////////////////// 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 &msg) { bool handled = false; switch (msg->what()) { case kWhatShutdown: { mCodec->deferMessage(msg); break; } case kWhatFlush: { // We're already doing this right now. handled = true; break; } default: handled = BaseState::onMessageReceived(msg); break; } return handled; } bool ACodec::FlushingState::onOMXEvent( OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { switch (event) { case OMX_EventCmdComplete: { CHECK_EQ(data1, (OMX_U32)OMX_CommandFlush); if (data2 == kPortIndexInput || data2 == kPortIndexOutput) { CHECK(!mFlushComplete[data2]); mFlushComplete[data2] = true; if (mFlushComplete[kPortIndexInput] && mFlushComplete[kPortIndexOutput]) { changeStateIfWeOwnAllBuffers(); } } else { CHECK_EQ(data2, OMX_ALL); CHECK(mFlushComplete[kPortIndexInput]); CHECK(mFlushComplete[kPortIndexOutput]); changeStateIfWeOwnAllBuffers(); } return true; } case OMX_EventPortSettingsChanged: { sp 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 &msg) { BaseState::onOutputBufferDrained(msg); changeStateIfWeOwnAllBuffers(); } void ACodec::FlushingState::onInputBufferFilled(const sp &msg) { BaseState::onInputBufferFilled(msg); changeStateIfWeOwnAllBuffers(); } void ACodec::FlushingState::changeStateIfWeOwnAllBuffers() { if (mFlushComplete[kPortIndexInput] && mFlushComplete[kPortIndexOutput] && mCodec->allYourBuffersAreBelongToUs()) { sp notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatFlushCompleted); notify->post(); mCodec->mPortEOS[kPortIndexInput] = mCodec->mPortEOS[kPortIndexOutput] = false; mCodec->changeState(mCodec->mExecutingState); } } } // namespace android