| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "ACodec" |
| |
| #include <media/stagefright/ACodec.h> |
| |
| #include <binder/MemoryDealer.h> |
| |
| #include <media/stagefright/foundation/hexdump.h> |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| |
| #include <media/stagefright/MediaCodecList.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/NativeWindowWrapper.h> |
| #include <media/stagefright/OMXClient.h> |
| #include <media/stagefright/OMXCodec.h> |
| |
| #include <media/hardware/HardwareAPI.h> |
| |
| #include <OMX_Component.h> |
| |
| #include "include/avc_utils.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); |
| virtual void stateEntered(); |
| |
| private: |
| void onSetup(const sp<AMessage> &msg); |
| bool onAllocateComponent(const sp<AMessage> &msg); |
| |
| DISALLOW_EVIL_CONSTRUCTORS(UninitializedState); |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| struct ACodec::LoadedState : public ACodec::BaseState { |
| LoadedState(ACodec *codec); |
| |
| protected: |
| virtual bool onMessageReceived(const sp<AMessage> &msg); |
| virtual void stateEntered(); |
| |
| private: |
| friend struct ACodec::UninitializedState; |
| |
| bool onConfigureComponent(const sp<AMessage> &msg); |
| void onStart(); |
| void onShutdown(bool keepComponentAllocated); |
| |
| DISALLOW_EVIL_CONSTRUCTORS(LoadedState); |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| 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(); |
| |
| bool mComponentNowIdle; |
| |
| 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::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() |
| : mQuirks(0), |
| mNode(NULL), |
| mSentFormat(false), |
| mIsEncoder(false), |
| mShutdownInProgress(false), |
| mEncoderDelay(0), |
| mEncoderPadding(0), |
| mChannelMaskPresent(false), |
| mChannelMask(0) { |
| mUninitializedState = new UninitializedState(this); |
| mLoadedState = new LoadedState(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; |
| mInputEOSResult = OK; |
| |
| 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::initiateAllocateComponent(const sp<AMessage> &msg) { |
| msg->setWhat(kWhatAllocateComponent); |
| msg->setTarget(id()); |
| msg->post(); |
| } |
| |
| void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) { |
| msg->setWhat(kWhatConfigureComponent); |
| msg->setTarget(id()); |
| msg->post(); |
| } |
| |
| void ACodec::initiateStart() { |
| (new AMessage(kWhatStart, id()))->post(); |
| } |
| |
| void ACodec::signalFlush() { |
| ALOGV("[%s] signalFlush", mComponentName.c_str()); |
| (new AMessage(kWhatFlush, id()))->post(); |
| } |
| |
| void ACodec::signalResume() { |
| (new AMessage(kWhatResume, id()))->post(); |
| } |
| |
| void ACodec::initiateShutdown(bool keepComponentAllocated) { |
| sp<AMessage> msg = new AMessage(kWhatShutdown, id()); |
| msg->setInt32("keepComponentAllocated", keepComponentAllocated); |
| msg->post(); |
| } |
| |
| void ACodec::signalRequestIDRFrame() { |
| (new AMessage(kWhatRequestIDRFrame, id()))->post(); |
| } |
| |
| status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { |
| CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput); |
| |
| CHECK(mDealer[portIndex] == NULL); |
| CHECK(mBuffers[portIndex].isEmpty()); |
| |
| status_t err; |
| if (mNativeWindow != NULL && portIndex == kPortIndexOutput) { |
| err = allocateOutputBuffersFromNativeWindow(); |
| } else { |
| OMX_PARAM_PORTDEFINITIONTYPE def; |
| InitOMXParams(&def); |
| def.nPortIndex = portIndex; |
| |
| err = mOMX->getParameter( |
| mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); |
| |
| if (err == OK) { |
| ALOGV("[%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, "ACodec"); |
| |
| for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) { |
| sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize); |
| CHECK(mem.get() != NULL); |
| |
| BufferInfo info; |
| info.mStatus = BufferInfo::OWNED_BY_US; |
| |
| uint32_t requiresAllocateBufferBit = |
| (portIndex == kPortIndexInput) |
| ? OMXCodec::kRequiresAllocateBufferOnInputPorts |
| : OMXCodec::kRequiresAllocateBufferOnOutputPorts; |
| |
| if (portIndex == kPortIndexInput && (mFlags & kFlagIsSecure)) { |
| mem.clear(); |
| |
| void *ptr; |
| err = mOMX->allocateBuffer( |
| mNode, portIndex, def.nBufferSize, &info.mBufferID, |
| &ptr); |
| |
| info.mData = new ABuffer(ptr, def.nBufferSize); |
| } else if (mQuirks & requiresAllocateBufferBit) { |
| err = mOMX->allocateBufferWithBackup( |
| mNode, portIndex, mem, &info.mBufferID); |
| } else { |
| err = mOMX->useBuffer(mNode, portIndex, mem, &info.mBufferID); |
| } |
| |
| if (mem != NULL) { |
| info.mData = new ABuffer(mem->pointer(), def.nBufferSize); |
| } |
| |
| mBuffers[portIndex].push(info); |
| } |
| } |
| } |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| sp<AMessage> notify = mNotify->dup(); |
| notify->setInt32("what", ACodec::kWhatBuffersAllocated); |
| |
| notify->setInt32("portIndex", portIndex); |
| |
| sp<PortDescription> desc = new PortDescription; |
| |
| for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) { |
| const BufferInfo &info = mBuffers[portIndex][i]; |
| |
| desc->addBuffer(info.mBufferID, info.mData); |
| } |
| |
| notify->setObject("portDesc", desc); |
| notify->post(); |
| |
| 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) { |
| ALOGE("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) { |
| ALOGW("querying usage flags from OMX IL component failed: %d", err); |
| // XXX: Currently this error is logged, but not fatal. |
| usage = 0; |
| } |
| |
| if (mFlags & kFlagIsSecure) { |
| usage |= GRALLOC_USAGE_PROTECTED; |
| } |
| |
| // Make sure to check whether either Stagefright or the video decoder |
| // requested protected buffers. |
| if (usage & GRALLOC_USAGE_PROTECTED) { |
| // Verify that the ANativeWindow sends images directly to |
| // SurfaceFlinger. |
| int queuesToNativeWindow = 0; |
| err = mNativeWindow->query( |
| mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, |
| &queuesToNativeWindow); |
| if (err != 0) { |
| ALOGE("error authenticating native window: %d", err); |
| return err; |
| } |
| if (queuesToNativeWindow != 1) { |
| ALOGE("native window could not be authenticated"); |
| return PERMISSION_DENIED; |
| } |
| } |
| |
| err = native_window_set_usage( |
| mNativeWindow.get(), |
| usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); |
| |
| if (err != 0) { |
| ALOGE("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) { |
| ALOGE("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) { |
| ALOGE("[%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) { |
| ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err), |
| -err); |
| return err; |
| } |
| |
| ALOGV("[%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 = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); |
| if (err != 0) { |
| ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); |
| break; |
| } |
| |
| sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false)); |
| BufferInfo info; |
| info.mStatus = BufferInfo::OWNED_BY_US; |
| info.mData = new ABuffer(0); |
| info.mGraphicBuffer = graphicBuffer; |
| mBuffers[kPortIndexOutput].push(info); |
| |
| IOMX::buffer_id bufferId; |
| err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer, |
| &bufferId); |
| if (err != 0) { |
| ALOGE("registering GraphicBuffer %lu with OMX IL component failed: " |
| "%d", i, err); |
| break; |
| } |
| |
| mBuffers[kPortIndexOutput].editItemAt(i).mBufferID = bufferId; |
| |
| ALOGV("[%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); |
| |
| ALOGV("[%s] Calling cancelBuffer on buffer %p", |
| mComponentName.c_str(), info->mBufferID); |
| |
| int err = mNativeWindow->cancelBuffer( |
| mNativeWindow.get(), info->mGraphicBuffer.get(), -1); |
| |
| CHECK_EQ(err, 0); |
| |
| info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; |
| |
| return OK; |
| } |
| |
| ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { |
| ANativeWindowBuffer *buf; |
| int fenceFd = -1; |
| if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { |
| ALOGE("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; |
| } |
| |
| status_t 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_MPEG_LAYER_I, |
| "audio_decoder.mp1", "audio_encoder.mp1" }, |
| { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II, |
| "audio_decoder.mp2", "audio_encoder.mp2" }, |
| { 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_AUDIO_VORBIS, |
| "audio_decoder.vorbis", "audio_encoder.vorbis" }, |
| { MEDIA_MIMETYPE_AUDIO_G711_MLAW, |
| "audio_decoder.g711mlaw", "audio_encoder.g711mlaw" }, |
| { MEDIA_MIMETYPE_AUDIO_G711_ALAW, |
| "audio_decoder.g711alaw", "audio_encoder.g711alaw" }, |
| { 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" }, |
| { MEDIA_MIMETYPE_VIDEO_VPX, |
| "video_decoder.vpx", "video_encoder.vpx" }, |
| { MEDIA_MIMETYPE_AUDIO_RAW, |
| "audio_decoder.raw", "audio_encoder.raw" }, |
| { MEDIA_MIMETYPE_AUDIO_FLAC, |
| "audio_decoder.flac", "audio_encoder.flac" }, |
| }; |
| |
| 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 ERROR_UNSUPPORTED; |
| } |
| |
| 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) { |
| ALOGW("[%s] Failed to set standard component role '%s'.", |
| mComponentName.c_str(), role); |
| |
| return err; |
| } |
| } |
| |
| return OK; |
| } |
| |
| status_t ACodec::configureCodec( |
| const char *mime, const sp<AMessage> &msg) { |
| int32_t encoder; |
| if (!msg->findInt32("encoder", &encoder)) { |
| encoder = false; |
| } |
| |
| mIsEncoder = encoder; |
| |
| status_t err = setComponentRole(encoder /* isEncoder */, mime); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| int32_t bitRate = 0; |
| // FLAC encoder doesn't need a bitrate, other encoders do |
| if (encoder && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC) |
| && !msg->findInt32("bitrate", &bitRate)) { |
| return INVALID_OPERATION; |
| } |
| |
| int32_t storeMeta; |
| if (encoder |
| && msg->findInt32("store-metadata-in-buffers", &storeMeta) |
| && storeMeta != 0) { |
| err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexInput, OMX_TRUE); |
| |
| if (err != OK) { |
| ALOGE("[%s] storeMetaDataInBuffers failed w/ err %d", |
| mComponentName.c_str(), err); |
| |
| return err; |
| } |
| } |
| |
| int32_t prependSPSPPS; |
| if (encoder |
| && msg->findInt32("prepend-sps-pps-to-idr-frames", &prependSPSPPS) |
| && prependSPSPPS != 0) { |
| OMX_INDEXTYPE index; |
| err = mOMX->getExtensionIndex( |
| mNode, |
| "OMX.google.android.index.prependSPSPPSToIDRFrames", |
| &index); |
| |
| if (err == OK) { |
| PrependSPSPPSToIDRFramesParams params; |
| InitOMXParams(¶ms); |
| params.bEnable = OMX_TRUE; |
| |
| err = mOMX->setParameter( |
| mNode, index, ¶ms, sizeof(params)); |
| } |
| |
| if (err != OK) { |
| ALOGE("Encoder could not be configured to emit SPS/PPS before " |
| "IDR frames. (err %d)", err); |
| |
| return err; |
| } |
| } |
| |
| if (!strncasecmp(mime, "video/", 6)) { |
| if (encoder) { |
| err = setupVideoEncoder(mime, msg); |
| } else { |
| int32_t width, height; |
| if (!msg->findInt32("width", &width) |
| || !msg->findInt32("height", &height)) { |
| err = INVALID_OPERATION; |
| } else { |
| err = setupVideoDecoder(mime, width, height); |
| } |
| } |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { |
| int32_t numChannels, sampleRate; |
| if (!msg->findInt32("channel-count", &numChannels) |
| || !msg->findInt32("sample-rate", &sampleRate)) { |
| err = INVALID_OPERATION; |
| } else { |
| int32_t isADTS, aacProfile; |
| if (!msg->findInt32("is-adts", &isADTS)) { |
| isADTS = 0; |
| } |
| if (!msg->findInt32("aac-profile", &aacProfile)) { |
| aacProfile = OMX_AUDIO_AACObjectNull; |
| } |
| |
| err = setupAACCodec( |
| encoder, numChannels, sampleRate, bitRate, aacProfile, |
| isADTS != 0); |
| } |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { |
| err = setupAMRCodec(encoder, false /* isWAMR */, bitRate); |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { |
| err = setupAMRCodec(encoder, true /* isWAMR */, bitRate); |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW) |
| || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) { |
| // These are PCM-like formats with a fixed sample rate but |
| // a variable number of channels. |
| |
| int32_t numChannels; |
| if (!msg->findInt32("channel-count", &numChannels)) { |
| err = INVALID_OPERATION; |
| } else { |
| err = setupG711Codec(encoder, numChannels); |
| } |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) { |
| int32_t numChannels, sampleRate, compressionLevel = -1; |
| if (encoder && |
| (!msg->findInt32("channel-count", &numChannels) |
| || !msg->findInt32("sample-rate", &sampleRate))) { |
| ALOGE("missing channel count or sample rate for FLAC encoder"); |
| err = INVALID_OPERATION; |
| } else { |
| if (encoder) { |
| if (!msg->findInt32("flac-compression-level", &compressionLevel)) { |
| compressionLevel = 5;// default FLAC compression level |
| } else if (compressionLevel < 0) { |
| ALOGW("compression level %d outside [0..8] range, using 0", compressionLevel); |
| compressionLevel = 0; |
| } else if (compressionLevel > 8) { |
| ALOGW("compression level %d outside [0..8] range, using 8", compressionLevel); |
| compressionLevel = 8; |
| } |
| } |
| err = setupFlacCodec(encoder, numChannels, sampleRate, compressionLevel); |
| } |
| } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { |
| int32_t numChannels, sampleRate; |
| if (encoder |
| || !msg->findInt32("channel-count", &numChannels) |
| || !msg->findInt32("sample-rate", &sampleRate)) { |
| err = INVALID_OPERATION; |
| } else { |
| err = setupRawAudioFormat(kPortIndexInput, sampleRate, numChannels); |
| } |
| } |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| if (!msg->findInt32("encoder-delay", &mEncoderDelay)) { |
| mEncoderDelay = 0; |
| } |
| |
| if (!msg->findInt32("encoder-padding", &mEncoderPadding)) { |
| mEncoderPadding = 0; |
| } |
| |
| if (msg->findInt32("channel-mask", &mChannelMask)) { |
| mChannelMaskPresent = true; |
| } else { |
| mChannelMaskPresent = false; |
| } |
| |
| int32_t maxInputSize; |
| if (msg->findInt32("max-input-size", &maxInputSize)) { |
| err = setMinBufferSize(kPortIndexInput, (size_t)maxInputSize); |
| } else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) { |
| err = setMinBufferSize(kPortIndexInput, 8192); // XXX |
| } |
| |
| return err; |
| } |
| |
| 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::selectAudioPortFormat( |
| OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat) { |
| OMX_AUDIO_PARAM_PORTFORMATTYPE format; |
| InitOMXParams(&format); |
| |
| format.nPortIndex = portIndex; |
| for (OMX_U32 index = 0;; ++index) { |
| format.nIndex = index; |
| |
| status_t err = mOMX->getParameter( |
| mNode, OMX_IndexParamAudioPortFormat, |
| &format, sizeof(format)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| if (format.eEncoding == desiredFormat) { |
| break; |
| } |
| } |
| |
| return mOMX->setParameter( |
| mNode, OMX_IndexParamAudioPortFormat, &format, sizeof(format)); |
| } |
| |
| status_t ACodec::setupAACCodec( |
| bool encoder, int32_t numChannels, int32_t sampleRate, |
| int32_t bitRate, int32_t aacProfile, bool isADTS) { |
| if (encoder && isADTS) { |
| return -EINVAL; |
| } |
| |
| status_t err = setupRawAudioFormat( |
| encoder ? kPortIndexInput : kPortIndexOutput, |
| sampleRate, |
| numChannels); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| if (encoder) { |
| err = selectAudioPortFormat(kPortIndexOutput, OMX_AUDIO_CodingAAC); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| OMX_PARAM_PORTDEFINITIONTYPE def; |
| InitOMXParams(&def); |
| def.nPortIndex = kPortIndexOutput; |
| |
| err = mOMX->getParameter( |
| mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| def.format.audio.bFlagErrorConcealment = OMX_TRUE; |
| def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| OMX_AUDIO_PARAM_AACPROFILETYPE profile; |
| InitOMXParams(&profile); |
| profile.nPortIndex = kPortIndexOutput; |
| |
| err = mOMX->getParameter( |
| mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| profile.nChannels = numChannels; |
| |
| profile.eChannelMode = |
| (numChannels == 1) |
| ? OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo; |
| |
| profile.nSampleRate = sampleRate; |
| profile.nBitRate = bitRate; |
| profile.nAudioBandWidth = 0; |
| profile.nFrameLength = 0; |
| profile.nAACtools = OMX_AUDIO_AACToolAll; |
| profile.nAACERtools = OMX_AUDIO_AACERNone; |
| profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile; |
| profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| return err; |
| } |
| |
| OMX_AUDIO_PARAM_AACPROFILETYPE profile; |
| InitOMXParams(&profile); |
| profile.nPortIndex = kPortIndexInput; |
| |
| err = mOMX->getParameter( |
| mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| profile.nChannels = numChannels; |
| profile.nSampleRate = sampleRate; |
| |
| profile.eAACStreamFormat = |
| isADTS |
| ? OMX_AUDIO_AACStreamFormatMP4ADTS |
| : OMX_AUDIO_AACStreamFormatMP4FF; |
| |
| return mOMX->setParameter( |
| mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); |
| } |
| |
| static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate( |
| bool isAMRWB, int32_t bps) { |
| if (isAMRWB) { |
| if (bps <= 6600) { |
| return OMX_AUDIO_AMRBandModeWB0; |
| } else if (bps <= 8850) { |
| return OMX_AUDIO_AMRBandModeWB1; |
| } else if (bps <= 12650) { |
| return OMX_AUDIO_AMRBandModeWB2; |
| } else if (bps <= 14250) { |
| return OMX_AUDIO_AMRBandModeWB3; |
| } else if (bps <= 15850) { |
| return OMX_AUDIO_AMRBandModeWB4; |
| } else if (bps <= 18250) { |
| return OMX_AUDIO_AMRBandModeWB5; |
| } else if (bps <= 19850) { |
| return OMX_AUDIO_AMRBandModeWB6; |
| } else if (bps <= 23050) { |
| return OMX_AUDIO_AMRBandModeWB7; |
| } |
| |
| // 23850 bps |
| return OMX_AUDIO_AMRBandModeWB8; |
| } else { // AMRNB |
| if (bps <= 4750) { |
| return OMX_AUDIO_AMRBandModeNB0; |
| } else if (bps <= 5150) { |
| return OMX_AUDIO_AMRBandModeNB1; |
| } else if (bps <= 5900) { |
| return OMX_AUDIO_AMRBandModeNB2; |
| } else if (bps <= 6700) { |
| return OMX_AUDIO_AMRBandModeNB3; |
| } else if (bps <= 7400) { |
| return OMX_AUDIO_AMRBandModeNB4; |
| } else if (bps <= 7950) { |
| return OMX_AUDIO_AMRBandModeNB5; |
| } else if (bps <= 10200) { |
| return OMX_AUDIO_AMRBandModeNB6; |
| } |
| |
| // 12200 bps |
| return OMX_AUDIO_AMRBandModeNB7; |
| } |
| } |
| |
| status_t ACodec::setupAMRCodec(bool encoder, bool isWAMR, int32_t bitrate) { |
| OMX_AUDIO_PARAM_AMRTYPE def; |
| InitOMXParams(&def); |
| def.nPortIndex = encoder ? kPortIndexOutput : kPortIndexInput; |
| |
| status_t err = |
| mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; |
| def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitrate); |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| return setupRawAudioFormat( |
| encoder ? kPortIndexInput : kPortIndexOutput, |
| isWAMR ? 16000 : 8000 /* sampleRate */, |
| 1 /* numChannels */); |
| } |
| |
| status_t ACodec::setupG711Codec(bool encoder, int32_t numChannels) { |
| CHECK(!encoder); // XXX TODO |
| |
| return setupRawAudioFormat( |
| kPortIndexInput, 8000 /* sampleRate */, numChannels); |
| } |
| |
| status_t ACodec::setupFlacCodec( |
| bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel) { |
| |
| if (encoder) { |
| OMX_AUDIO_PARAM_FLACTYPE def; |
| InitOMXParams(&def); |
| def.nPortIndex = kPortIndexOutput; |
| |
| // configure compression level |
| status_t err = mOMX->getParameter(mNode, OMX_IndexParamAudioFlac, &def, sizeof(def)); |
| if (err != OK) { |
| ALOGE("setupFlacCodec(): Error %d getting OMX_IndexParamAudioFlac parameter", err); |
| return err; |
| } |
| def.nCompressionLevel = compressionLevel; |
| err = mOMX->setParameter(mNode, OMX_IndexParamAudioFlac, &def, sizeof(def)); |
| if (err != OK) { |
| ALOGE("setupFlacCodec(): Error %d setting OMX_IndexParamAudioFlac parameter", err); |
| return err; |
| } |
| } |
| |
| return setupRawAudioFormat( |
| encoder ? kPortIndexInput : kPortIndexOutput, |
| sampleRate, |
| numChannels); |
| } |
| |
| status_t ACodec::setupRawAudioFormat( |
| OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) { |
| 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; |
| } |
| |
| def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| OMX_AUDIO_PARAM_PCMMODETYPE pcmParams; |
| InitOMXParams(&pcmParams); |
| pcmParams.nPortIndex = portIndex; |
| |
| err = mOMX->getParameter( |
| mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| pcmParams.nChannels = numChannels; |
| pcmParams.eNumData = OMX_NumericalDataSigned; |
| pcmParams.bInterleaved = OMX_TRUE; |
| pcmParams.nBitPerSample = 16; |
| pcmParams.nSamplingRate = sampleRate; |
| pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear; |
| |
| if (getOMXChannelMapping(numChannels, pcmParams.eChannelMapping) != OK) { |
| return OMX_ErrorNone; |
| } |
| |
| return mOMX->setParameter( |
| mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); |
| } |
| |
| 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 |
| || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka); |
| |
| return mOMX->setParameter( |
| mNode, OMX_IndexParamVideoPortFormat, |
| &format, sizeof(format)); |
| } |
| |
| static status_t GetVideoCodingTypeFromMime( |
| const char *mime, OMX_VIDEO_CODINGTYPE *codingType) { |
| if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { |
| *codingType = OMX_VIDEO_CodingAVC; |
| } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { |
| *codingType = OMX_VIDEO_CodingMPEG4; |
| } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { |
| *codingType = OMX_VIDEO_CodingH263; |
| } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) { |
| *codingType = OMX_VIDEO_CodingMPEG2; |
| } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) { |
| *codingType = OMX_VIDEO_CodingVPX; |
| } else { |
| *codingType = OMX_VIDEO_CodingUnused; |
| return ERROR_UNSUPPORTED; |
| } |
| |
| return OK; |
| } |
| |
| status_t ACodec::setupVideoDecoder( |
| const char *mime, int32_t width, int32_t height) { |
| OMX_VIDEO_CODINGTYPE compressionFormat; |
| status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| 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::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) { |
| int32_t tmp; |
| if (!msg->findInt32("color-format", &tmp)) { |
| return INVALID_OPERATION; |
| } |
| |
| OMX_COLOR_FORMATTYPE colorFormat = |
| static_cast<OMX_COLOR_FORMATTYPE>(tmp); |
| |
| status_t err = setVideoPortFormatType( |
| kPortIndexInput, OMX_VIDEO_CodingUnused, colorFormat); |
| |
| if (err != OK) { |
| ALOGE("[%s] does not support color format %d", |
| mComponentName.c_str(), colorFormat); |
| |
| return err; |
| } |
| |
| /* Input port configuration */ |
| |
| OMX_PARAM_PORTDEFINITIONTYPE def; |
| InitOMXParams(&def); |
| |
| OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; |
| |
| def.nPortIndex = kPortIndexInput; |
| |
| err = mOMX->getParameter( |
| mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| int32_t width, height, bitrate; |
| if (!msg->findInt32("width", &width) |
| || !msg->findInt32("height", &height) |
| || !msg->findInt32("bitrate", &bitrate)) { |
| return INVALID_OPERATION; |
| } |
| |
| video_def->nFrameWidth = width; |
| video_def->nFrameHeight = height; |
| |
| int32_t stride; |
| if (!msg->findInt32("stride", &stride)) { |
| stride = width; |
| } |
| |
| video_def->nStride = stride; |
| |
| int32_t sliceHeight; |
| if (!msg->findInt32("slice-height", &sliceHeight)) { |
| sliceHeight = height; |
| } |
| |
| video_def->nSliceHeight = sliceHeight; |
| |
| def.nBufferSize = (video_def->nStride * video_def->nSliceHeight * 3) / 2; |
| |
| float frameRate; |
| if (!msg->findFloat("frame-rate", &frameRate)) { |
| int32_t tmp; |
| if (!msg->findInt32("frame-rate", &tmp)) { |
| return INVALID_OPERATION; |
| } |
| frameRate = (float)tmp; |
| } |
| |
| video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f); |
| video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; |
| video_def->eColorFormat = colorFormat; |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); |
| |
| if (err != OK) { |
| ALOGE("[%s] failed to set input port definition parameters.", |
| mComponentName.c_str()); |
| |
| return err; |
| } |
| |
| /* Output port configuration */ |
| |
| OMX_VIDEO_CODINGTYPE compressionFormat; |
| err = GetVideoCodingTypeFromMime(mime, &compressionFormat); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| err = setVideoPortFormatType( |
| kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused); |
| |
| if (err != OK) { |
| ALOGE("[%s] does not support compression format %d", |
| mComponentName.c_str(), compressionFormat); |
| |
| return err; |
| } |
| |
| def.nPortIndex = kPortIndexOutput; |
| |
| err = mOMX->getParameter( |
| mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| video_def->nFrameWidth = width; |
| video_def->nFrameHeight = height; |
| video_def->xFramerate = 0; |
| video_def->nBitrate = bitrate; |
| video_def->eCompressionFormat = compressionFormat; |
| video_def->eColorFormat = OMX_COLOR_FormatUnused; |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); |
| |
| if (err != OK) { |
| ALOGE("[%s] failed to set output port definition parameters.", |
| mComponentName.c_str()); |
| |
| return err; |
| } |
| |
| switch (compressionFormat) { |
| case OMX_VIDEO_CodingMPEG4: |
| err = setupMPEG4EncoderParameters(msg); |
| break; |
| |
| case OMX_VIDEO_CodingH263: |
| err = setupH263EncoderParameters(msg); |
| break; |
| |
| case OMX_VIDEO_CodingAVC: |
| err = setupAVCEncoderParameters(msg); |
| break; |
| |
| default: |
| break; |
| } |
| |
| ALOGI("setupVideoEncoder succeeded"); |
| |
| return err; |
| } |
| |
| status_t ACodec::setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode) { |
| OMX_VIDEO_PARAM_INTRAREFRESHTYPE params; |
| InitOMXParams(¶ms); |
| params.nPortIndex = kPortIndexOutput; |
| |
| params.eRefreshMode = static_cast<OMX_VIDEO_INTRAREFRESHTYPE>(mode); |
| |
| if (params.eRefreshMode == OMX_VIDEO_IntraRefreshCyclic || |
| params.eRefreshMode == OMX_VIDEO_IntraRefreshBoth) { |
| int32_t mbs; |
| if (!msg->findInt32("intra-refresh-CIR-mbs", &mbs)) { |
| return INVALID_OPERATION; |
| } |
| params.nCirMBs = mbs; |
| } |
| |
| if (params.eRefreshMode == OMX_VIDEO_IntraRefreshAdaptive || |
| params.eRefreshMode == OMX_VIDEO_IntraRefreshBoth) { |
| int32_t mbs; |
| if (!msg->findInt32("intra-refresh-AIR-mbs", &mbs)) { |
| return INVALID_OPERATION; |
| } |
| params.nAirMBs = mbs; |
| |
| int32_t ref; |
| if (!msg->findInt32("intra-refresh-AIR-ref", &ref)) { |
| return INVALID_OPERATION; |
| } |
| params.nAirRef = ref; |
| } |
| |
| status_t err = mOMX->setParameter( |
| mNode, OMX_IndexParamVideoIntraRefresh, |
| ¶ms, sizeof(params)); |
| return err; |
| } |
| |
| static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) { |
| if (iFramesInterval < 0) { |
| return 0xFFFFFFFF; |
| } else if (iFramesInterval == 0) { |
| return 0; |
| } |
| OMX_U32 ret = frameRate * iFramesInterval; |
| CHECK(ret > 1); |
| return ret; |
| } |
| |
| static OMX_VIDEO_CONTROLRATETYPE getBitrateMode(const sp<AMessage> &msg) { |
| int32_t tmp; |
| if (!msg->findInt32("bitrate-mode", &tmp)) { |
| return OMX_Video_ControlRateVariable; |
| } |
| |
| return static_cast<OMX_VIDEO_CONTROLRATETYPE>(tmp); |
| } |
| |
| status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) { |
| int32_t bitrate, iFrameInterval; |
| if (!msg->findInt32("bitrate", &bitrate) |
| || !msg->findInt32("i-frame-interval", &iFrameInterval)) { |
| return INVALID_OPERATION; |
| } |
| |
| OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg); |
| |
| float frameRate; |
| if (!msg->findFloat("frame-rate", &frameRate)) { |
| int32_t tmp; |
| if (!msg->findInt32("frame-rate", &tmp)) { |
| return INVALID_OPERATION; |
| } |
| frameRate = (float)tmp; |
| } |
| |
| OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type; |
| InitOMXParams(&mpeg4type); |
| mpeg4type.nPortIndex = kPortIndexOutput; |
| |
| status_t err = mOMX->getParameter( |
| mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| mpeg4type.nSliceHeaderSpacing = 0; |
| mpeg4type.bSVH = OMX_FALSE; |
| mpeg4type.bGov = OMX_FALSE; |
| |
| mpeg4type.nAllowedPictureTypes = |
| OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; |
| |
| mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate); |
| if (mpeg4type.nPFrames == 0) { |
| mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; |
| } |
| mpeg4type.nBFrames = 0; |
| mpeg4type.nIDCVLCThreshold = 0; |
| mpeg4type.bACPred = OMX_TRUE; |
| mpeg4type.nMaxPacketSize = 256; |
| mpeg4type.nTimeIncRes = 1000; |
| mpeg4type.nHeaderExtension = 0; |
| mpeg4type.bReversibleVLC = OMX_FALSE; |
| |
| int32_t profile; |
| if (msg->findInt32("profile", &profile)) { |
| int32_t level; |
| if (!msg->findInt32("level", &level)) { |
| return INVALID_OPERATION; |
| } |
| |
| err = verifySupportForProfileAndLevel(profile, level); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profile); |
| mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(level); |
| } |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| err = configureBitrate(bitrate, bitrateMode); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| return setupErrorCorrectionParameters(); |
| } |
| |
| status_t ACodec::setupH263EncoderParameters(const sp<AMessage> &msg) { |
| int32_t bitrate, iFrameInterval; |
| if (!msg->findInt32("bitrate", &bitrate) |
| || !msg->findInt32("i-frame-interval", &iFrameInterval)) { |
| return INVALID_OPERATION; |
| } |
| |
| OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg); |
| |
| float frameRate; |
| if (!msg->findFloat("frame-rate", &frameRate)) { |
| int32_t tmp; |
| if (!msg->findInt32("frame-rate", &tmp)) { |
| return INVALID_OPERATION; |
| } |
| frameRate = (float)tmp; |
| } |
| |
| OMX_VIDEO_PARAM_H263TYPE h263type; |
| InitOMXParams(&h263type); |
| h263type.nPortIndex = kPortIndexOutput; |
| |
| status_t err = mOMX->getParameter( |
| mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| h263type.nAllowedPictureTypes = |
| OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; |
| |
| h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate); |
| if (h263type.nPFrames == 0) { |
| h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; |
| } |
| h263type.nBFrames = 0; |
| |
| int32_t profile; |
| if (msg->findInt32("profile", &profile)) { |
| int32_t level; |
| if (!msg->findInt32("level", &level)) { |
| return INVALID_OPERATION; |
| } |
| |
| err = verifySupportForProfileAndLevel(profile, level); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profile); |
| h263type.eLevel = static_cast<OMX_VIDEO_H263LEVELTYPE>(level); |
| } |
| |
| h263type.bPLUSPTYPEAllowed = OMX_FALSE; |
| h263type.bForceRoundingTypeToZero = OMX_FALSE; |
| h263type.nPictureHeaderRepetition = 0; |
| h263type.nGOBHeaderInterval = 0; |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| err = configureBitrate(bitrate, bitrateMode); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| return setupErrorCorrectionParameters(); |
| } |
| |
| status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) { |
| int32_t bitrate, iFrameInterval; |
| if (!msg->findInt32("bitrate", &bitrate) |
| || !msg->findInt32("i-frame-interval", &iFrameInterval)) { |
| return INVALID_OPERATION; |
| } |
| |
| OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg); |
| |
| float frameRate; |
| if (!msg->findFloat("frame-rate", &frameRate)) { |
| int32_t tmp; |
| if (!msg->findInt32("frame-rate", &tmp)) { |
| return INVALID_OPERATION; |
| } |
| frameRate = (float)tmp; |
| } |
| |
| status_t err = OK; |
| int32_t intraRefreshMode = 0; |
| if (msg->findInt32("intra-refresh-mode", &intraRefreshMode)) { |
| err = setCyclicIntraMacroblockRefresh(msg, intraRefreshMode); |
| if (err != OK) { |
| ALOGE("Setting intra macroblock refresh mode (%d) failed: 0x%x", |
| err, intraRefreshMode); |
| return err; |
| } |
| } |
| |
| OMX_VIDEO_PARAM_AVCTYPE h264type; |
| InitOMXParams(&h264type); |
| h264type.nPortIndex = kPortIndexOutput; |
| |
| err = mOMX->getParameter( |
| mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| h264type.nAllowedPictureTypes = |
| OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; |
| |
| int32_t profile; |
| if (msg->findInt32("profile", &profile)) { |
| int32_t level; |
| if (!msg->findInt32("level", &level)) { |
| return INVALID_OPERATION; |
| } |
| |
| err = verifySupportForProfileAndLevel(profile, level); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profile); |
| h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(level); |
| } |
| |
| // XXX |
| if (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline) { |
| ALOGW("Use baseline profile instead of %d for AVC recording", |
| h264type.eProfile); |
| h264type.eProfile = OMX_VIDEO_AVCProfileBaseline; |
| } |
| |
| if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) { |
| h264type.nSliceHeaderSpacing = 0; |
| h264type.bUseHadamard = OMX_TRUE; |
| h264type.nRefFrames = 1; |
| h264type.nBFrames = 0; |
| h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate); |
| if (h264type.nPFrames == 0) { |
| h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; |
| } |
| h264type.nRefIdx10ActiveMinus1 = 0; |
| h264type.nRefIdx11ActiveMinus1 = 0; |
| h264type.bEntropyCodingCABAC = OMX_FALSE; |
| h264type.bWeightedPPrediction = OMX_FALSE; |
| h264type.bconstIpred = OMX_FALSE; |
| h264type.bDirect8x8Inference = OMX_FALSE; |
| h264type.bDirectSpatialTemporal = OMX_FALSE; |
| h264type.nCabacInitIdc = 0; |
| } |
| |
| if (h264type.nBFrames != 0) { |
| h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB; |
| } |
| |
| h264type.bEnableUEP = OMX_FALSE; |
| h264type.bEnableFMO = OMX_FALSE; |
| h264type.bEnableASO = OMX_FALSE; |
| h264type.bEnableRS = OMX_FALSE; |
| h264type.bFrameMBsOnly = OMX_TRUE; |
| h264type.bMBAFF = OMX_FALSE; |
| h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable; |
| |
| err = mOMX->setParameter( |
| mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| return configureBitrate(bitrate, bitrateMode); |
| } |
| |
| status_t ACodec::verifySupportForProfileAndLevel( |
| int32_t profile, int32_t level) { |
| OMX_VIDEO_PARAM_PROFILELEVELTYPE params; |
| InitOMXParams(¶ms); |
| params.nPortIndex = kPortIndexOutput; |
| |
| for (params.nProfileIndex = 0;; ++params.nProfileIndex) { |
| status_t err = mOMX->getParameter( |
| mNode, |
| OMX_IndexParamVideoProfileLevelQuerySupported, |
| ¶ms, |
| sizeof(params)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| int32_t supportedProfile = static_cast<int32_t>(params.eProfile); |
| int32_t supportedLevel = static_cast<int32_t>(params.eLevel); |
| |
| if (profile == supportedProfile && level <= supportedLevel) { |
| return OK; |
| } |
| } |
| } |
| |
| status_t ACodec::configureBitrate( |
| int32_t bitrate, OMX_VIDEO_CONTROLRATETYPE bitrateMode) { |
| OMX_VIDEO_PARAM_BITRATETYPE bitrateType; |
| InitOMXParams(&bitrateType); |
| bitrateType.nPortIndex = kPortIndexOutput; |
| |
| status_t err = mOMX->getParameter( |
| mNode, OMX_IndexParamVideoBitrate, |
| &bitrateType, sizeof(bitrateType)); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| bitrateType.eControlRate = bitrateMode; |
| bitrateType.nTargetBitrate = bitrate; |
| |
| return mOMX->setParameter( |
| mNode, OMX_IndexParamVideoBitrate, |
| &bitrateType, sizeof(bitrateType)); |
| } |
| |
| status_t ACodec::setupErrorCorrectionParameters() { |
| OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType; |
| InitOMXParams(&errorCorrectionType); |
| errorCorrectionType.nPortIndex = kPortIndexOutput; |
| |
| status_t err = mOMX->getParameter( |
| mNode, OMX_IndexParamVideoErrorCorrection, |
| &errorCorrectionType, sizeof(errorCorrectionType)); |
| |
| if (err != OK) { |
| return OK; // Optional feature. Ignore this failure |
| } |
| |
| errorCorrectionType.bEnableHEC = OMX_FALSE; |
| errorCorrectionType.bEnableResync = OMX_TRUE; |
| errorCorrectionType.nResynchMarkerSpacing = 256; |
| errorCorrectionType.bEnableDataPartitioning = OMX_FALSE; |
| errorCorrectionType.bEnableRVLC = OMX_FALSE; |
| |
| return mOMX->setParameter( |
| mNode, OMX_IndexParamVideoErrorCorrection, |
| &errorCorrectionType, sizeof(errorCorrectionType)); |
| } |
| |
| 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; |
| } |
| |
| size_t ACodec::countBuffersOwnedByComponent(OMX_U32 portIndex) const { |
| size_t n = 0; |
| |
| for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) { |
| const BufferInfo &info = mBuffers[portIndex].itemAt(i); |
| |
| if (info.mStatus == BufferInfo::OWNED_BY_COMPONENT) { |
| ++n; |
| } |
| } |
| |
| return n; |
| } |
| |
| 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) { |
| ALOGV("[%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); |
| notify->setInt32("stride", videoDef->nStride); |
| notify->setInt32("slice-height", videoDef->nSliceHeight); |
| notify->setInt32("color-format", videoDef->eColorFormat); |
| |
| 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); |
| if (mEncoderDelay + mEncoderPadding) { |
| size_t frameSize = params.nChannels * sizeof(int16_t); |
| if (mSkipCutBuffer != NULL) { |
| size_t prevbufsize = mSkipCutBuffer->size(); |
| if (prevbufsize != 0) { |
| ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbufsize); |
| } |
| } |
| mSkipCutBuffer = new SkipCutBuffer(mEncoderDelay * frameSize, |
| mEncoderPadding * frameSize); |
| } |
| |
| if (mChannelMaskPresent) { |
| notify->setInt32("channel-mask", mChannelMask); |
| } |
| |
| break; |
| } |
| |
| default: |
| TRESPASS(); |
| } |
| |
| notify->post(); |
| |
| mSentFormat = true; |
| } |
| |
| void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) { |
| sp<AMessage> notify = mNotify->dup(); |
| notify->setInt32("what", ACodec::kWhatError); |
| notify->setInt32("omx-error", error); |
| notify->setInt32("err", internalError); |
| notify->post(); |
| } |
| |
| status_t ACodec::pushBlankBuffersToNativeWindow() { |
| status_t err = NO_ERROR; |
| ANativeWindowBuffer* anb = NULL; |
| int numBufs = 0; |
| int minUndequeuedBufs = 0; |
| |
| // We need to reconnect to the ANativeWindow as a CPU client to ensure that |
| // no frames get dropped by SurfaceFlinger assuming that these are video |
| // frames. |
| err = native_window_api_disconnect(mNativeWindow.get(), |
| NATIVE_WINDOW_API_MEDIA); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", |
| strerror(-err), -err); |
| return err; |
| } |
| |
| err = native_window_api_connect(mNativeWindow.get(), |
| NATIVE_WINDOW_API_CPU); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: api_connect failed: %s (%d)", |
| strerror(-err), -err); |
| return err; |
| } |
| |
| err = native_window_set_buffers_geometry(mNativeWindow.get(), 1, 1, |
| HAL_PIXEL_FORMAT_RGBX_8888); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: set_buffers_geometry failed: %s (%d)", |
| strerror(-err), -err); |
| goto error; |
| } |
| |
| err = native_window_set_usage(mNativeWindow.get(), |
| GRALLOC_USAGE_SW_WRITE_OFTEN); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: set_usage failed: %s (%d)", |
| strerror(-err), -err); |
| goto error; |
| } |
| |
| err = mNativeWindow->query(mNativeWindow.get(), |
| NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query " |
| "failed: %s (%d)", strerror(-err), -err); |
| goto error; |
| } |
| |
| numBufs = minUndequeuedBufs + 1; |
| err = native_window_set_buffer_count(mNativeWindow.get(), numBufs); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)", |
| strerror(-err), -err); |
| goto error; |
| } |
| |
| // We push numBufs + 1 buffers to ensure that we've drawn into the same |
| // buffer twice. This should guarantee that the buffer has been displayed |
| // on the screen and then been replaced, so an previous video frames are |
| // guaranteed NOT to be currently displayed. |
| for (int i = 0; i < numBufs + 1; i++) { |
| int fenceFd = -1; |
| err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", |
| strerror(-err), -err); |
| goto error; |
| } |
| |
| sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); |
| |
| // Fill the buffer with the a 1x1 checkerboard pattern ;) |
| uint32_t* img = NULL; |
| err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: lock failed: %s (%d)", |
| strerror(-err), -err); |
| goto error; |
| } |
| |
| *img = 0; |
| |
| err = buf->unlock(); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: unlock failed: %s (%d)", |
| strerror(-err), -err); |
| goto error; |
| } |
| |
| err = mNativeWindow->queueBuffer(mNativeWindow.get(), |
| buf->getNativeBuffer(), -1); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", |
| strerror(-err), -err); |
| goto error; |
| } |
| |
| anb = NULL; |
| } |
| |
| error: |
| |
| if (err != NO_ERROR) { |
| // Clean up after an error. |
| if (anb != NULL) { |
| mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1); |
| } |
| |
| native_window_api_disconnect(mNativeWindow.get(), |
| NATIVE_WINDOW_API_CPU); |
| native_window_api_connect(mNativeWindow.get(), |
| NATIVE_WINDOW_API_MEDIA); |
| |
| return err; |
| } else { |
| // Clean up after success. |
| err = native_window_api_disconnect(mNativeWindow.get(), |
| NATIVE_WINDOW_API_CPU); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", |
| strerror(-err), -err); |
| return err; |
| } |
| |
| err = native_window_api_connect(mNativeWindow.get(), |
| NATIVE_WINDOW_API_MEDIA); |
| if (err != NO_ERROR) { |
| ALOGE("error pushing blank frames: api_connect failed: %s (%d)", |
| strerror(-err), -err); |
| return err; |
| } |
| |
| return NO_ERROR; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ACodec::PortDescription::PortDescription() { |
| } |
| |
| status_t ACodec::requestIDRFrame() { |
| if (!mIsEncoder) { |
| return ERROR_UNSUPPORTED; |
| } |
| |
| OMX_CONFIG_INTRAREFRESHVOPTYPE params; |
| InitOMXParams(¶ms); |
| |
| params.nPortIndex = kPortIndexOutput; |
| params.IntraRefreshVOP = OMX_TRUE; |
| |
| return mOMX->setConfig( |
| mNode, |
| OMX_IndexConfigVideoIntraVOPRefresh, |
| ¶ms, |
| sizeof(params)); |
| } |
| |
| void ACodec::PortDescription::addBuffer( |
| IOMX::buffer_id id, const sp<ABuffer> &buffer) { |
| mBufferIDs.push_back(id); |
| mBuffers.push_back(buffer); |
| } |
| |
| size_t ACodec::PortDescription::countBuffers() { |
| return mBufferIDs.size(); |
| } |
| |
| IOMX::buffer_id ACodec::PortDescription::bufferIDAt(size_t index) const { |
| return mBufferIDs.itemAt(index); |
| } |
| |
| sp<ABuffer> ACodec::PortDescription::bufferAt(size_t index) const { |
| return mBuffers.itemAt(index); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState) |
| : AState(parentState), |
| mCodec(codec) { |
| } |
| |
| ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(OMX_U32 portIndex) { |
| return KEEP_BUFFERS; |
| } |
| |
| bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) { |
| switch (msg->what()) { |
| case kWhatInputBufferFilled: |
| { |
| onInputBufferFilled(msg); |
| break; |
| } |
| |
| case kWhatOutputBufferDrained: |
| { |
| onOutputBufferDrained(msg); |
| break; |
| } |
| |
| case ACodec::kWhatOMXMessage: |
| { |
| return onOMXMessage(msg); |
| } |
| |
| default: |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { |
| int32_t type; |
| CHECK(msg->findInt32("type", &type)); |
| |
| IOMX::node_id nodeID; |
| CHECK(msg->findPointer("node", &nodeID)); |
| CHECK_EQ(nodeID, mCodec->mNode); |
| |
| switch (type) { |
| case omx_message::EVENT: |
| { |
| int32_t event, data1, data2; |
| CHECK(msg->findInt32("event", &event)); |
| CHECK(msg->findInt32("data1", &data1)); |
| CHECK(msg->findInt32("data2", &data2)); |
| |
| if (event == OMX_EventCmdComplete |
| && data1 == OMX_CommandFlush |
| && data2 == (int32_t)OMX_ALL) { |
| // Use of this notification is not consistent across |
| // implementations. We'll drop this notification and rely |
| // on flush-complete notifications on the individual port |
| // indices instead. |
| |
| return true; |
| } |
| |
| return onOMXEvent( |
| static_cast<OMX_EVENTTYPE>(event), |
| static_cast<OMX_U32>(data1), |
| static_cast<OMX_U32>(data2)); |
| } |
| |
| case omx_message::EMPTY_BUFFER_DONE: |
| { |
| IOMX::buffer_id bufferID; |
| CHECK(msg->findPointer("buffer", &bufferID)); |
| |
| return onOMXEmptyBufferDone(bufferID); |
| } |
| |
| case omx_message::FILL_BUFFER_DONE: |
| { |
| IOMX::buffer_id bufferID; |
| CHECK(msg->findPointer("buffer", &bufferID)); |
| |
| int32_t rangeOffset, rangeLength, flags; |
| int64_t timeUs; |
| void *platformPrivate; |
| void *dataPtr; |
| |
| CHECK(msg->findInt32("range_offset", &rangeOffset)); |
| CHECK(msg->findInt32("range_length", &rangeLength)); |
| CHECK(msg->findInt32("flags", &flags)); |
| CHECK(msg->findInt64("timestamp", &timeUs)); |
| CHECK(msg->findPointer("platform_private", &platformPrivate)); |
| CHECK(msg->findPointer("data_ptr", &dataPtr)); |
| |
| return onOMXFillBufferDone( |
| bufferID, |
| (size_t)rangeOffset, (size_t)rangeLength, |
| (OMX_U32)flags, |
| timeUs, |
| platformPrivate, |
| dataPtr); |
| } |
| |
| default: |
| TRESPASS(); |
| break; |
| } |
| } |
| |
| bool ACodec::BaseState::onOMXEvent( |
| OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { |
| if (event != OMX_EventError) { |
| ALOGV("[%s] EVENT(%d, 0x%08lx, 0x%08lx)", |
| mCodec->mComponentName.c_str(), event, data1, data2); |
| |
| return false; |
| } |
| |
| ALOGE("[%s] ERROR(0x%08lx)", mCodec->mComponentName.c_str(), data1); |
| |
| mCodec->signalError((OMX_ERRORTYPE)data1); |
| |
| return true; |
| } |
| |
| bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) { |
| ALOGV("[%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; |
| |
| const sp<AMessage> &bufferMeta = info->mData->meta(); |
| void *mediaBuffer; |
| if (bufferMeta->findPointer("mediaBuffer", &mediaBuffer) |
| && mediaBuffer != NULL) { |
| // We're in "store-metadata-in-buffers" mode, the underlying |
| // OMX component had access to data that's implicitly refcounted |
| // by this "mediaBuffer" object. Now that the OMX component has |
| // told us that it's done with the input buffer, we can decrement |
| // the mediaBuffer's reference count. |
| |
| ALOGV("releasing mbuf %p", mediaBuffer); |
| |
| ((MediaBuffer *)mediaBuffer)->release(); |
| mediaBuffer = NULL; |
| |
| bufferMeta->setPointer("mediaBuffer", NULL); |
| } |
| |
| 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->setBuffer("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<ABuffer> buffer; |
| int32_t err = OK; |
| bool eos = false; |
| |
| if (!msg->findBuffer("buffer", &buffer)) { |
| CHECK(msg->findInt32("err", &err)); |
| |
| ALOGV("[%s] saw error %d instead of an input buffer", |
| mCodec->mComponentName.c_str(), err); |
| |
| buffer.clear(); |
| |
| eos = true; |
| } |
| |
| int32_t tmp; |
| if (buffer != NULL && buffer->meta()->findInt32("eos", &tmp) && tmp) { |
| eos = true; |
| err = ERROR_END_OF_STREAM; |
| } |
| |
| 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 (eos) { |
| if (!mCodec->mPortEOS[kPortIndexInput]) { |
| mCodec->mPortEOS[kPortIndexInput] = true; |
| mCodec->mInputEOSResult = err; |
| } |
| } |
| break; |
| } |
| |
| case RESUBMIT_BUFFERS: |
| { |
| if (buffer != NULL && !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 (eos) { |
| flags |= OMX_BUFFERFLAG_EOS; |
| } |
| |
| if (buffer != info->mData) { |
| ALOGV("[%s] Needs to copy input data for buffer %p. (%p != %p)", |
| mCodec->mComponentName.c_str(), |
| bufferID, |
| buffer.get(), info->mData.get()); |
| |
| CHECK_LE(buffer->size(), info->mData->capacity()); |
| memcpy(info->mData->data(), buffer->data(), buffer->size()); |
| } |
| |
| if (flags & OMX_BUFFERFLAG_CODECCONFIG) { |
| ALOGV("[%s] calling emptyBuffer %p w/ codec specific data", |
| mCodec->mComponentName.c_str(), bufferID); |
| } else if (flags & OMX_BUFFERFLAG_EOS) { |
| ALOGV("[%s] calling emptyBuffer %p w/ EOS", |
| mCodec->mComponentName.c_str(), bufferID); |
| } else { |
| #if TRACK_BUFFER_TIMING |
| ALOGI("[%s] calling emptyBuffer %p w/ time %lld us", |
| mCodec->mComponentName.c_str(), bufferID, timeUs); |
| #else |
| ALOGV("[%s] calling emptyBuffer %p w/ time %lld us", |
| mCodec->mComponentName.c_str(), bufferID, timeUs); |
| #endif |
| } |
| |
| #if TRACK_BUFFER_TIMING |
| ACodec::BufferStats stats; |
| stats.mEmptyBufferTimeUs = ALooper::GetNowUs(); |
| stats.mFillBufferDoneTimeUs = -1ll; |
| mCodec->mBufferStats.add(timeUs, stats); |
| #endif |
| |
| CHECK_EQ(mCodec->mOMX->emptyBuffer( |
| mCodec->mNode, |
| bufferID, |
| 0, |
| buffer->size(), |
| flags, |
| timeUs), |
| (status_t)OK); |
| |
| info->mStatus = BufferInfo::OWNED_BY_COMPONENT; |
| |
| if (!eos) { |
| getMoreInputDataIfPossible(); |
| } else { |
| ALOGV("[%s] Signalled EOS on the input port", |
| mCodec->mComponentName.c_str()); |
| |
| mCodec->mPortEOS[kPortIndexInput] = true; |
| mCodec->mInputEOSResult = err; |
| } |
| } else if (!mCodec->mPortEOS[kPortIndexInput]) { |
| if (err != ERROR_END_OF_STREAM) { |
| ALOGV("[%s] Signalling EOS on the input port " |
| "due to error %d", |
| mCodec->mComponentName.c_str(), err); |
| } else { |
| ALOGV("[%s] Signalling EOS on the input port", |
| mCodec->mComponentName.c_str()); |
| } |
| |
| ALOGV("[%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; |
| mCodec->mInputEOSResult = err; |
| } |
| 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) { |
| ALOGV("[%s] onOMXFillBufferDone %p time %lld us, flags = 0x%08lx", |
| mCodec->mComponentName.c_str(), bufferID, timeUs, flags); |
| |
| ssize_t index; |
| |
| #if TRACK_BUFFER_TIMING |
| index = mCodec->mBufferStats.indexOfKey(timeUs); |
| if (index >= 0) { |
| ACodec::BufferStats *stats = &mCodec->mBufferStats.editValueAt(index); |
| stats->mFillBufferDoneTimeUs = ALooper::GetNowUs(); |
| |
| ALOGI("frame PTS %lld: %lld", |
| timeUs, |
| stats->mFillBufferDoneTimeUs - stats->mEmptyBufferTimeUs); |
| |
| mCodec->mBufferStats.removeItemsAt(index); |
| stats = NULL; |
| } |
| #endif |
| |
| 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 && !(flags & OMX_BUFFERFLAG_EOS)) { |
| ALOGV("[%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; |
| } |
| |
| if (!mCodec->mIsEncoder && !mCodec->mSentFormat) { |
| mCodec->sendFormatChange(); |
| } |
| |
| if (mCodec->mNativeWindow == NULL) { |
| info->mData->setRange(rangeOffset, rangeLength); |
| |
| #if 0 |
| if (IsIDR(info->mData)) { |
| ALOGI("IDR frame"); |
| } |
| #endif |
| } |
| |
| if (mCodec->mSkipCutBuffer != NULL) { |
| mCodec->mSkipCutBuffer->submit(info->mData); |
| } |
| info->mData->meta()->setInt64("timeUs", timeUs); |
| |
| sp<AMessage> notify = mCodec->mNotify->dup(); |
| notify->setInt32("what", ACodec::kWhatDrainThisBuffer); |
| notify->setPointer("buffer-id", info->mBufferID); |
| notify->setBuffer("buffer", info->mData); |
| notify->setInt32("flags", flags); |
| |
| 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) { |
| ALOGV("[%s] saw output EOS", mCodec->mComponentName.c_str()); |
| |
| sp<AMessage> notify = mCodec->mNotify->dup(); |
| notify->setInt32("what", ACodec::kWhatEOS); |
| notify->setInt32("err", mCodec->mInputEOSResult); |
| 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. |
| |
| status_t err; |
| if ((err = mCodec->mNativeWindow->queueBuffer( |
| mCodec->mNativeWindow.get(), |
| info->mGraphicBuffer.get(), -1)) == OK) { |
| info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; |
| } else { |
| mCodec->signalError(OMX_ErrorUndefined, err); |
| 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) { |
| ALOGV("[%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; |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| |