diff options
-rw-r--r-- | include/media/IOMX.h | 19 | ||||
-rw-r--r-- | include/media/stagefright/ACodec.h | 14 | ||||
-rw-r--r-- | media/libmedia/IOMX.cpp | 32 | ||||
-rw-r--r-- | media/libstagefright/ACodec.cpp | 165 | ||||
-rw-r--r-- | media/libstagefright/OMXClient.cpp | 12 | ||||
-rw-r--r-- | media/libstagefright/include/OMX.h | 8 | ||||
-rw-r--r-- | media/libstagefright/include/OMXNodeInstance.h | 17 | ||||
-rw-r--r-- | media/libstagefright/omx/GraphicBufferSource.cpp | 50 | ||||
-rw-r--r-- | media/libstagefright/omx/GraphicBufferSource.h | 3 | ||||
-rw-r--r-- | media/libstagefright/omx/OMX.cpp | 15 | ||||
-rw-r--r-- | media/libstagefright/omx/OMXNodeInstance.cpp | 105 | ||||
-rw-r--r-- | media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp | 10 |
12 files changed, 352 insertions, 98 deletions
diff --git a/include/media/IOMX.h b/include/media/IOMX.h index 26cc73e..84fdf83 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -92,7 +92,7 @@ public: node_id node, OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) = 0; - virtual status_t configureVideoTunnelMode( + virtual status_t configureVideoTunnelMode( node_id node, OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync, native_handle_t **sidebandHandle) = 0; @@ -152,13 +152,23 @@ public: virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer) = 0; - virtual status_t fillBuffer(node_id node, buffer_id buffer) = 0; - + enum { + kFenceTimeoutMs = 1000 + }; + // Calls OMX_FillBuffer on buffer, and passes |fenceFd| to component if it supports + // fences. Otherwise, it waits on |fenceFd| before calling OMX_FillBuffer. + // Takes ownership of |fenceFd| even if this call fails. + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd = -1) = 0; + + // Calls OMX_EmptyBuffer on buffer (after updating buffer header with |range_offset|, + // |range_length|, |flags| and |timestamp|). Passes |fenceFd| to component if it + // supports fences. Otherwise, it waits on |fenceFd| before calling OMX_EmptyBuffer. + // Takes ownership of |fenceFd| even if this call fails. virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) = 0; + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd = -1) = 0; virtual status_t getExtensionIndex( node_id node, @@ -190,6 +200,7 @@ struct omx_message { } type; IOMX::node_id node; + int fenceFd; // used for EMPTY_BUFFER_DONE and FILL_BUFFER_DONE; client must close this union { // if type == EVENT diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index bbecc80..f7a3df7 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -160,11 +160,25 @@ private: sp<ABuffer> mData; sp<GraphicBuffer> mGraphicBuffer; + int mFenceFd; + + // The following field and 4 methods are used for debugging only + bool mIsReadFence; + // Store |fenceFd| and set read/write flag. Log error, if there is already a fence stored. + void setReadFence(int fenceFd, const char *dbg); + void setWriteFence(int fenceFd, const char *dbg); + // Log error, if the current fence is not a read/write fence. + void checkReadFence(const char *dbg); + void checkWriteFence(const char *dbg); }; static const char *_asString(BufferInfo::Status s); void dumpBuffers(OMX_U32 portIndex); + // If |fd| is non-negative, waits for fence with |fd| and logs an error if it fails. Returns + // the error code or OK on success. If |fd| is negative, it returns OK + status_t waitForFence(int fd, const char *dbg); + #if TRACK_BUFFER_TIMING struct BufferStats { int64_t mEmptyBufferTimeUs; diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index cac2f7f..ca1cdc7 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -511,11 +511,15 @@ public: return reply.readInt32(); } - virtual status_t fillBuffer(node_id node, buffer_id buffer) { + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); data.writeInt32((int32_t)buffer); + data.writeInt32(fenceFd >= 0); + if (fenceFd >= 0) { + data.writeFileDescriptor(fenceFd, true /* takeOwnership */); + } remote()->transact(FILL_BUFFER, data, &reply); return reply.readInt32(); @@ -525,7 +529,7 @@ public: node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); data.writeInt32((int32_t)node); @@ -534,6 +538,10 @@ public: data.writeInt32(range_length); data.writeInt32(flags); data.writeInt64(timestamp); + data.writeInt32(fenceFd >= 0); + if (fenceFd >= 0) { + data.writeFileDescriptor(fenceFd, true /* takeOwnership */); + } remote()->transact(EMPTY_BUFFER, data, &reply); return reply.readInt32(); @@ -1012,7 +1020,9 @@ status_t BnOMX::onTransact( node_id node = (node_id)data.readInt32(); buffer_id buffer = (buffer_id)data.readInt32(); - reply->writeInt32(fillBuffer(node, buffer)); + bool haveFence = data.readInt32(); + int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1; + reply->writeInt32(fillBuffer(node, buffer, fenceFd)); return NO_ERROR; } @@ -1027,11 +1037,10 @@ status_t BnOMX::onTransact( OMX_U32 range_length = data.readInt32(); OMX_U32 flags = data.readInt32(); OMX_TICKS timestamp = data.readInt64(); - - reply->writeInt32( - emptyBuffer( - node, buffer, range_offset, range_length, - flags, timestamp)); + bool haveFence = data.readInt32(); + int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1; + reply->writeInt32(emptyBuffer( + node, buffer, range_offset, range_length, flags, timestamp, fenceFd)); return NO_ERROR; } @@ -1072,7 +1081,9 @@ public: Parcel data, reply; data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor()); data.write(&msg, sizeof(msg)); - + if (msg.fenceFd >= 0) { + data.writeFileDescriptor(msg.fenceFd, true /* takeOwnership */); + } ALOGV("onMessage writing message %d, size %zu", msg.type, sizeof(msg)); remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY); @@ -1090,6 +1101,9 @@ status_t BnOMXObserver::onTransact( omx_message msg; data.read(&msg, sizeof(msg)); + if (msg.fenceFd >= 0) { + msg.fenceFd = ::dup(data.readFileDescriptor()); + } ALOGV("onTransact reading message %d, size %zu", msg.type, sizeof(msg)); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 08045d1..a770746 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -132,6 +132,7 @@ struct CodecObserver : public BnOMXObserver { case omx_message::EMPTY_BUFFER_DONE: { msg->setInt32("buffer", omx_msg.u.buffer_data.buffer); + msg->setInt32("fence_fd", omx_msg.fenceFd); break; } @@ -151,6 +152,8 @@ struct CodecObserver : public BnOMXObserver { msg->setInt64( "timestamp", omx_msg.u.extended_buffer_data.timestamp); + msg->setInt32( + "fence_fd", omx_msg.fenceFd); break; } @@ -199,13 +202,14 @@ protected: private: bool onOMXMessage(const sp<AMessage> &msg); - bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID); + bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd); bool onOMXFillBufferDone( IOMX::buffer_id bufferID, size_t rangeOffset, size_t rangeLength, OMX_U32 flags, - int64_t timeUs); + int64_t timeUs, + int fenceFd); void getMoreInputDataIfPossible(); @@ -407,6 +411,38 @@ private: //////////////////////////////////////////////////////////////////////////////// +void ACodec::BufferInfo::setWriteFence(int fenceFd, const char *dbg) { + if (mFenceFd >= 0) { + ALOGW("OVERWRITE OF %s fence %d by write fence %d in %s", + mIsReadFence ? "read" : "write", mFenceFd, fenceFd, dbg); + } + mFenceFd = fenceFd; + mIsReadFence = false; +} + +void ACodec::BufferInfo::setReadFence(int fenceFd, const char *dbg) { + if (mFenceFd >= 0) { + ALOGW("OVERWRITE OF %s fence %d by read fence %d in %s", + mIsReadFence ? "read" : "write", mFenceFd, fenceFd, dbg); + } + mFenceFd = fenceFd; + mIsReadFence = true; +} + +void ACodec::BufferInfo::checkWriteFence(const char *dbg) { + if (mFenceFd >= 0 && mIsReadFence) { + ALOGD("REUSING read fence %d as write fence in %s", mFenceFd, dbg); + } +} + +void ACodec::BufferInfo::checkReadFence(const char *dbg) { + if (mFenceFd >= 0 && !mIsReadFence) { + ALOGD("REUSING write fence %d as read fence in %s", mFenceFd, dbg); + } +} + +//////////////////////////////////////////////////////////////////////////////// + ACodec::ACodec() : mQuirks(0), mNode(0), @@ -640,11 +676,12 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { // cancel undequeued buffers to new surface if (!storingMetadataInDecodedBuffers() || mLegacyAdaptiveExperiment) { for (size_t i = 0; i < buffers.size(); ++i) { - const BufferInfo &info = buffers[i]; + BufferInfo &info = buffers.editItemAt(i); if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { ALOGV("canceling buffer %p", info.mGraphicBuffer->getNativeBuffer()); err = nativeWindow->cancelBuffer( - nativeWindow, info.mGraphicBuffer->getNativeBuffer(), -1); + nativeWindow, info.mGraphicBuffer->getNativeBuffer(), info.mFenceFd); + info.mFenceFd = -1; if (err != OK) { ALOGE("failed to cancel buffer %p to the new surface: %s (%d)", info.mGraphicBuffer->getNativeBuffer(), @@ -721,6 +758,7 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; + info.mFenceFd = -1; uint32_t requiresAllocateBufferBit = (portIndex == kPortIndexInput) @@ -925,7 +963,8 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { // Dequeue buffers and send them to OMX for (OMX_U32 i = 0; i < bufferCount; i++) { ANativeWindowBuffer *buf; - err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); + int fenceFd; + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd); if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; @@ -934,6 +973,8 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false)); BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; + info.mFenceFd = fenceFd; + info.mIsReadFence = false; info.mData = new ABuffer(NULL /* data */, bufferSize /* capacity */); info.mGraphicBuffer = graphicBuffer; mBuffers[kPortIndexOutput].push(info); @@ -1004,6 +1045,7 @@ status_t ACodec::allocateOutputMetadataBuffers() { for (OMX_U32 i = 0; i < bufferCount; i++) { BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; + info.mFenceFd = -1; info.mGraphicBuffer = NULL; info.mDequeuedAt = mDequeueCounter; @@ -1040,7 +1082,8 @@ status_t ACodec::allocateOutputMetadataBuffers() { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); ANativeWindowBuffer *buf; - err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); + int fenceFd; + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd); if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; @@ -1050,6 +1093,7 @@ status_t ACodec::allocateOutputMetadataBuffers() { mOMX->updateGraphicBufferInMeta( mNode, kPortIndexOutput, graphicBuffer, info->mBufferID); info->mStatus = BufferInfo::OWNED_BY_US; + info->setWriteFence(fenceFd, "allocateOutputMetadataBuffers for legacy"); info->mGraphicBuffer = graphicBuffer; } @@ -1083,7 +1127,9 @@ status_t ACodec::submitOutputMetadataBuffer() { mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get()); --mMetadataBuffersToSubmit; - status_t err = mOMX->fillBuffer(mNode, info->mBufferID); + info->checkWriteFence("submitOutputMetadataBuffer"); + status_t err = mOMX->fillBuffer(mNode, info->mBufferID, info->mFenceFd); + info->mFenceFd = -1; if (err == OK) { info->mStatus = BufferInfo::OWNED_BY_COMPONENT; } @@ -1091,6 +1137,16 @@ status_t ACodec::submitOutputMetadataBuffer() { return err; } +status_t ACodec::waitForFence(int fd, const char *dbg ) { + status_t res = OK; + if (fd >= 0) { + sp<Fence> fence = new Fence(fd); + res = fence->wait(IOMX::kFenceTimeoutMs); + ALOGW_IF(res != OK, "FENCE TIMEOUT for %d in %s", fd, dbg); + } + return res; +} + // static const char *ACodec::_asString(BufferInfo::Status s) { switch (s) { @@ -1123,8 +1179,10 @@ status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { ALOGV("[%s] Calling cancelBuffer on buffer %u", mComponentName.c_str(), info->mBufferID); + info->checkWriteFence("cancelBufferToNativeWindow"); int err = mNativeWindow->cancelBuffer( - mNativeWindow.get(), info->mGraphicBuffer.get(), -1); + mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd); + info->mFenceFd = -1; ALOGW_IF(err != 0, "[%s] can not return buffer %u to native window", mComponentName.c_str(), info->mBufferID); @@ -1144,9 +1202,11 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { return NULL; } + int fenceFd = -1; do { - if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { - ALOGE("dequeueBuffer failed."); + status_t err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd); + if (err != 0) { + ALOGE("dequeueBuffer failed: %s(%d).", asString(err), err); return NULL; } @@ -1170,7 +1230,7 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { } ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer()); info->mStatus = BufferInfo::OWNED_BY_US; - + info->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow"); return info; } } @@ -1213,6 +1273,7 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { // discard buffer in LRU info and replace with new buffer oldest->mGraphicBuffer = new GraphicBuffer(buf, false); oldest->mStatus = BufferInfo::OWNED_BY_US; + oldest->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow for oldest"); mOMX->updateGraphicBufferInMeta( mNode, kPortIndexOutput, oldest->mGraphicBuffer, @@ -1277,6 +1338,18 @@ status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) { BufferInfo *info = &mBuffers[portIndex].editItemAt(i); status_t err = OK; + // there should not be any fences in the metadata + MetadataBufferType type = + portIndex == kPortIndexOutput ? mOutputMetadataType : mInputMetadataType; + if (type == kMetadataBufferTypeANWBuffer && info->mData != NULL + && info->mData->size() >= sizeof(VideoNativeMetadata)) { + int fenceFd = ((VideoNativeMetadata *)info->mData->data())->nFenceFd; + if (fenceFd >= 0) { + ALOGW("unreleased fence (%d) in %s metadata buffer %zu", + fenceFd, portIndex == kPortIndexInput ? "input" : "output", i); + } + } + switch (info->mStatus) { case BufferInfo::OWNED_BY_US: if (portIndex == kPortIndexOutput && mNativeWindow != NULL) { @@ -1294,6 +1367,10 @@ status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) { break; } + if (info->mFenceFd >= 0) { + ::close(info->mFenceFd); + } + // remove buffer even if mOMX->freeBuffer fails mBuffers[portIndex].removeAt(i); @@ -4433,9 +4510,12 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { case omx_message::EMPTY_BUFFER_DONE: { IOMX::buffer_id bufferID; + int32_t fenceFd; + CHECK(msg->findInt32("buffer", (int32_t*)&bufferID)); + CHECK(msg->findInt32("fence_fd", &fenceFd)); - return onOMXEmptyBufferDone(bufferID); + return onOMXEmptyBufferDone(bufferID, fenceFd); } case omx_message::FILL_BUFFER_DONE: @@ -4443,19 +4523,21 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) { IOMX::buffer_id bufferID; CHECK(msg->findInt32("buffer", (int32_t*)&bufferID)); - int32_t rangeOffset, rangeLength, flags; + int32_t rangeOffset, rangeLength, flags, fenceFd; int64_t timeUs; CHECK(msg->findInt32("range_offset", &rangeOffset)); CHECK(msg->findInt32("range_length", &rangeLength)); CHECK(msg->findInt32("flags", &flags)); CHECK(msg->findInt64("timestamp", &timeUs)); + CHECK(msg->findInt32("fence_fd", &fenceFd)); return onOMXFillBufferDone( bufferID, (size_t)rangeOffset, (size_t)rangeLength, (OMX_U32)flags, - timeUs); + timeUs, + fenceFd); } default: @@ -4486,7 +4568,7 @@ bool ACodec::BaseState::onOMXEvent( return true; } -bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) { +bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd) { ALOGV("[%s] onOMXEmptyBufferDone %u", mCodec->mComponentName.c_str(), bufferID); @@ -4495,10 +4577,20 @@ bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) { if (status != BufferInfo::OWNED_BY_COMPONENT) { ALOGE("Wrong ownership in EBD: %s(%d) buffer #%u", _asString(status), status, bufferID); mCodec->dumpBuffers(kPortIndexInput); + if (fenceFd >= 0) { + ::close(fenceFd); + } return false; } info->mStatus = BufferInfo::OWNED_BY_US; + // input buffers cannot take fences, so wait for any fence now + (void)mCodec->waitForFence(fenceFd, "onOMXEmptyBufferDone"); + fenceFd = -1; + + // still save fence for completeness + info->setWriteFence(fenceFd, "onOMXEmptyBufferDone"); + // 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 @@ -4670,13 +4762,16 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { mCodec->submitOutputMetadataBuffer(); } } + info->checkReadFence("onInputBufferFilled"); status_t err2 = mCodec->mOMX->emptyBuffer( mCodec->mNode, bufferID, 0, buffer->size(), flags, - timeUs); + timeUs, + info->mFenceFd); + info->mFenceFd = -1; if (err2 != OK) { mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2)); return; @@ -4704,13 +4799,16 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { ALOGV("[%s] calling emptyBuffer %u signalling EOS", mCodec->mComponentName.c_str(), bufferID); + info->checkReadFence("onInputBufferFilled"); status_t err2 = mCodec->mOMX->emptyBuffer( mCodec->mNode, bufferID, 0, 0, OMX_BUFFERFLAG_EOS, - 0); + 0, + info->mFenceFd); + info->mFenceFd = -1; if (err2 != OK) { mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2)); return; @@ -4765,7 +4863,8 @@ bool ACodec::BaseState::onOMXFillBufferDone( IOMX::buffer_id bufferID, size_t rangeOffset, size_t rangeLength, OMX_U32 flags, - int64_t timeUs) { + int64_t timeUs, + int fenceFd) { ALOGV("[%s] onOMXFillBufferDone %u time %" PRId64 " us, flags = 0x%08x", mCodec->mComponentName.c_str(), bufferID, timeUs, flags); @@ -4794,12 +4893,22 @@ bool ACodec::BaseState::onOMXFillBufferDone( ALOGE("Wrong ownership in FBD: %s(%d) buffer #%u", _asString(status), status, bufferID); mCodec->dumpBuffers(kPortIndexOutput); mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION); + if (fenceFd >= 0) { + ::close(fenceFd); + } return true; } info->mDequeuedAt = ++mCodec->mDequeueCounter; info->mStatus = BufferInfo::OWNED_BY_US; + // byte buffers cannot take fences, so wait for any fence now + if (mCodec->mNativeWindow == NULL) { + (void)mCodec->waitForFence(fenceFd, "onOMXFillBufferDone"); + fenceFd = -1; + } + info->setReadFence(fenceFd, "onOMXFillBufferDone"); + PortMode mode = getPortMode(kPortIndexOutput); switch (mode) { @@ -4813,7 +4922,8 @@ bool ACodec::BaseState::onOMXFillBufferDone( ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID); - err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID); + err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID, info->mFenceFd); + info->mFenceFd = -1; if (err != OK) { mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err)); return true; @@ -4946,17 +5056,23 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs); ALOGW_IF(err != NO_ERROR, "failed to set buffer timestamp: %d", err); + info->checkReadFence("onOutputBufferDrained before queueBuffer"); err = mCodec->mNativeWindow->queueBuffer( - mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), -1); + mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd); + info->mFenceFd = -1; if (err == OK) { info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; } else { mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err)); info->mStatus = BufferInfo::OWNED_BY_US; + // keeping read fence as write fence to avoid clobbering + info->mIsReadFence = false; } } else { if (mCodec->mNativeWindow != NULL && (info->mData == NULL || info->mData->size() != 0)) { + // move read fence into write fence to avoid clobbering + info->mIsReadFence = false; ATRACE_NAME("frame-drop"); } info->mStatus = BufferInfo::OWNED_BY_US; @@ -4991,7 +5107,10 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { if (info != NULL) { ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID); - status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID); + info->checkWriteFence("onOutputBufferDrained::RESUBMIT_BUFFERS"); + status_t err = mCodec->mOMX->fillBuffer( + mCodec->mNode, info->mBufferID, info->mFenceFd); + info->mFenceFd = -1; if (err == OK) { info->mStatus = BufferInfo::OWNED_BY_COMPONENT; } else { @@ -5754,7 +5873,9 @@ void ACodec::ExecutingState::submitRegularOutputBuffers() { ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID); - status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID); + info->checkWriteFence("submitRegularOutputBuffers"); + status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID, info->mFenceFd); + info->mFenceFd = -1; if (err != OK) { failed = true; break; diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index 5d04628..e69890d 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -125,13 +125,13 @@ struct MuxOMX : public IOMX { virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer); - virtual status_t fillBuffer(node_id node, buffer_id buffer); + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd); virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp); + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); virtual status_t getExtensionIndex( node_id node, @@ -385,17 +385,17 @@ status_t MuxOMX::freeBuffer( return getOMX(node)->freeBuffer(node, port_index, buffer); } -status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer) { - return getOMX(node)->fillBuffer(node, buffer); +status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer, int fenceFd) { + return getOMX(node)->fillBuffer(node, buffer, fenceFd); } status_t MuxOMX::emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { return getOMX(node)->emptyBuffer( - node, buffer, range_offset, range_length, flags, timestamp); + node, buffer, range_offset, range_length, flags, timestamp, fenceFd); } status_t MuxOMX::getExtensionIndex( diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index c34954b..d468dfc 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -118,13 +118,13 @@ public: virtual status_t freeBuffer( node_id node, OMX_U32 port_index, buffer_id buffer); - virtual status_t fillBuffer(node_id node, buffer_id buffer); + virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd); virtual status_t emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp); + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); virtual status_t getExtensionIndex( node_id node, @@ -148,10 +148,10 @@ public: OMX_IN OMX_PTR pEventData); OMX_ERRORTYPE OnEmptyBufferDone( - node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd); OMX_ERRORTYPE OnFillBufferDone( - node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer); + node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd); void invalidateNodeID(node_id node); diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h index fe6dccd..76df815 100644 --- a/media/libstagefright/include/OMXNodeInstance.h +++ b/media/libstagefright/include/OMXNodeInstance.h @@ -105,16 +105,16 @@ struct OMXNodeInstance { status_t freeBuffer(OMX_U32 portIndex, OMX::buffer_id buffer); - status_t fillBuffer(OMX::buffer_id buffer); + status_t fillBuffer(OMX::buffer_id buffer, int fenceFd); status_t emptyBuffer( OMX::buffer_id buffer, OMX_U32 rangeOffset, OMX_U32 rangeLength, - OMX_U32 flags, OMX_TICKS timestamp); + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); status_t emptyGraphicBuffer( OMX_BUFFERHEADERTYPE *header, const sp<GraphicBuffer> &buffer, - OMX_U32 flags, OMX_TICKS timestamp); + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd); status_t getExtensionIndex( const char *parameterName, OMX_INDEXTYPE *index); @@ -208,9 +208,18 @@ private: status_t storeMetaDataInBuffers_l( OMX_U32 portIndex, OMX_BOOL enable, MetadataBufferType *type); + // Stores fence into buffer if it is ANWBuffer type and has enough space. + // otherwise, waits for the fence to signal. Takes ownership of |fenceFd|. + status_t storeFenceInMeta_l( + OMX_BUFFERHEADERTYPE *header, int fenceFd, OMX_U32 portIndex); + + // Retrieves the fence from buffer if ANWBuffer type and has enough space. Otherwise, returns -1 + int retrieveFenceFromMeta_l( + OMX_BUFFERHEADERTYPE *header, OMX_U32 portIndex); + status_t emptyBuffer_l( OMX_BUFFERHEADERTYPE *header, - OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr); + OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr, int fenceFd); status_t updateGraphicBufferInMeta_l( OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer, diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index f797e63..be91510 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -64,6 +64,7 @@ GraphicBufferSource::GraphicBufferSource( mLatestBufferId(-1), mLatestBufferFrameNum(0), mLatestBufferUseCount(0), + mLatestBufferFence(Fence::NO_FENCE), mRepeatBufferDeferred(false), mTimePerCaptureUs(-1ll), mTimePerFrameUs(-1ll), @@ -226,9 +227,8 @@ void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) { mCodecBuffers.add(codecBuffer); } -void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { +void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header, int fenceFd) { Mutex::Autolock autoLock(mMutex); - if (!mExecuting) { return; } @@ -237,6 +237,9 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { if (cbi < 0) { // This should never happen. ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header); + if (fenceFd >= 0) { + ::close(fenceFd); + } return; } @@ -258,6 +261,9 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { } // No GraphicBuffer to deal with, no additional input or output is // expected, so just return. + if (fenceFd >= 0) { + ::close(fenceFd); + } return; } @@ -291,6 +297,7 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { // If we find a match, release that slot. If we don't, the BufferQueue // has dropped that GraphicBuffer, and there's nothing for us to release. int id = codecBuffer.mBuf; + sp<Fence> fence = new Fence(fenceFd); if (mBufferSlot[id] != NULL && mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { ALOGV("cbi %d matches bq slot %d, handle=%p", @@ -304,15 +311,16 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { int outSlot; mConsumer->attachBuffer(&outSlot, mBufferSlot[id]); mConsumer->releaseBuffer(outSlot, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, fence); } else { mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, fence); } } } else { ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d", cbi); + // we will not reuse codec buffer, so there is no need to wait for fence } // Mark the codec buffer as available by clearing the GraphicBuffer ref. @@ -394,7 +402,7 @@ void GraphicBufferSource::suspend(bool suspend) { mConsumer->detachBuffer(item.mBuf); mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer); mConsumer->releaseBuffer(item.mBuf, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } else { mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); @@ -447,13 +455,6 @@ bool GraphicBufferSource::fillCodecBuffer_l() { mNumFramesAvailable--; - // Wait for it to become available. - err = item.mFence->waitForever("GraphicBufferSource::fillCodecBuffer_l"); - if (err != OK) { - ALOGW("failed to wait for buffer fence: %d", err); - // keep going - } - // If this is the first time we're seeing this buffer, add it to our // slot table. if (item.mGraphicBuffer != NULL) { @@ -489,11 +490,12 @@ bool GraphicBufferSource::fillCodecBuffer_l() { mConsumer->detachBuffer(item.mBuf); mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer); mConsumer->releaseBuffer(item.mBuf, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } else { mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } + // item.mFence is released at the end of this method } else { ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); setLatestBuffer_l(item, dropped); @@ -520,9 +522,10 @@ bool GraphicBufferSource::repeatLatestBuffer_l() { mLatestBufferFrameNum, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, - Fence::NO_FENCE); + mLatestBufferFence); mLatestBufferId = -1; mLatestBufferFrameNum = 0; + mLatestBufferFence = Fence::NO_FENCE; return false; } @@ -537,6 +540,7 @@ bool GraphicBufferSource::repeatLatestBuffer_l() { item.mBuf = mLatestBufferId; item.mFrameNumber = mLatestBufferFrameNum; item.mTimestamp = mRepeatLastFrameTimestamp; + item.mFence = mLatestBufferFence; status_t err = submitBuffer_l(item, cbi); @@ -576,12 +580,13 @@ void GraphicBufferSource::setLatestBuffer_l( mConsumer->attachBuffer(&outSlot, mBufferSlot[mLatestBufferId]); mConsumer->releaseBuffer(outSlot, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, mLatestBufferFence); } else { mConsumer->releaseBuffer( mLatestBufferId, mLatestBufferFrameNum, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, mLatestBufferFence); } + // mLatestBufferFence will be set to new fence just below } } @@ -592,6 +597,7 @@ void GraphicBufferSource::setLatestBuffer_l( mLatestBufferUseCount = dropped ? 0 : 1; mRepeatBufferDeferred = false; mRepeatLastFrameCount = kRepeatLastFrameCount; + mLatestBufferFence = item.mFence; if (mReflector != NULL) { sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector); @@ -687,8 +693,7 @@ int64_t GraphicBufferSource::getTimestamp(const BufferItem &item) { return timeUs; } -status_t GraphicBufferSource::submitBuffer_l( - const BufferItem &item, int cbi) { +status_t GraphicBufferSource::submitBuffer_l(const BufferItem &item, int cbi) { ALOGV("submitBuffer_l cbi=%d", cbi); int64_t timeUs = getTimestamp(item); @@ -704,7 +709,8 @@ status_t GraphicBufferSource::submitBuffer_l( OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; sp<GraphicBuffer> buffer = codecBuffer.mGraphicBuffer; status_t err = mNodeInstance->emptyGraphicBuffer( - header, buffer, OMX_BUFFERFLAG_ENDOFFRAME, timeUs); + header, buffer, OMX_BUFFERFLAG_ENDOFFRAME, timeUs, + item.mFence->isValid() ? item.mFence->dup() : -1); if (err != OK) { ALOGW("WARNING: emptyNativeWindowBuffer failed: 0x%x", err); codecBuffer.mGraphicBuffer = NULL; @@ -737,7 +743,7 @@ void GraphicBufferSource::submitEndOfInputStream_l() { OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; status_t err = mNodeInstance->emptyGraphicBuffer( header, NULL /* buffer */, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS, - 0 /* timestamp */); + 0 /* timestamp */, -1 /* fenceFd */); if (err != OK) { ALOGW("emptyDirectBuffer EOS failed: 0x%x", err); } else { @@ -799,7 +805,7 @@ void GraphicBufferSource::onFrameAvailable(const BufferItem& /*item*/) { mConsumer->detachBuffer(item.mBuf); mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer); mConsumer->releaseBuffer(item.mBuf, 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); } else { mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 21ee96a..555bbec 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -93,7 +93,7 @@ public: // Called from OnEmptyBufferDone. If we have a BQ buffer available, // fill it with a new frame of data; otherwise, just mark it as available. - void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header); + void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header, int fenceFd); // Called when omx_message::FILL_BUFFER_DONE is received. (Currently the // buffer source will fix timestamp in the header if needed.) @@ -274,6 +274,7 @@ private: int mLatestBufferId; uint64_t mLatestBufferFrameNum; int32_t mLatestBufferUseCount; + sp<Fence> mLatestBufferFence; // The previous buffer should've been repeated but // no codec buffer was available at the time. diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 4ca827c..76217ec 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -415,17 +415,17 @@ status_t OMX::freeBuffer(node_id node, OMX_U32 port_index, buffer_id buffer) { port_index, buffer); } -status_t OMX::fillBuffer(node_id node, buffer_id buffer) { - return findInstance(node)->fillBuffer(buffer); +status_t OMX::fillBuffer(node_id node, buffer_id buffer, int fenceFd) { + return findInstance(node)->fillBuffer(buffer, fenceFd); } status_t OMX::emptyBuffer( node_id node, buffer_id buffer, OMX_U32 range_offset, OMX_U32 range_length, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { return findInstance(node)->emptyBuffer( - buffer, range_offset, range_length, flags, timestamp); + buffer, range_offset, range_length, flags, timestamp, fenceFd); } status_t OMX::getExtensionIndex( @@ -459,6 +459,7 @@ OMX_ERRORTYPE OMX::OnEvent( omx_message msg; msg.type = omx_message::EVENT; msg.node = node; + msg.fenceFd = -1; msg.u.event_data.event = eEvent; msg.u.event_data.data1 = nData1; msg.u.event_data.data2 = nData2; @@ -469,12 +470,13 @@ OMX_ERRORTYPE OMX::OnEvent( } OMX_ERRORTYPE OMX::OnEmptyBufferDone( - node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { + node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) { ALOGV("OnEmptyBufferDone buffer=%p", pBuffer); omx_message msg; msg.type = omx_message::EMPTY_BUFFER_DONE; msg.node = node; + msg.fenceFd = fenceFd; msg.u.buffer_data.buffer = buffer; findDispatcher(node)->post(msg); @@ -483,12 +485,13 @@ OMX_ERRORTYPE OMX::OnEmptyBufferDone( } OMX_ERRORTYPE OMX::OnFillBufferDone( - node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) { + node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) { ALOGV("OnFillBufferDone buffer=%p", pBuffer); omx_message msg; msg.type = omx_message::FILL_BUFFER_DONE; msg.node = node; + msg.fenceFd = fenceFd; msg.u.extended_buffer_data.buffer = buffer; msg.u.extended_buffer_data.range_offset = pBuffer->nOffset; msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen; diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index e4b2de4..4ba4aeb 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -76,11 +76,11 @@ static const OMX_U32 kPortIndexOutput = 1; #define SIMPLE_NEW_BUFFER(buffer_id, port, size, data) \ NEW_BUFFER_FMT(buffer_id, port, "%zu@%p", (size), (data)) -#define EMPTY_BUFFER(addr, header) "%#x [%u@%p]", \ - (addr), (header)->nAllocLen, (header)->pBuffer -#define FULL_BUFFER(addr, header) "%#" PRIxPTR " [%u@%p (%u..+%u) f=%x ts=%lld]", \ +#define EMPTY_BUFFER(addr, header, fenceFd) "%#x [%u@%p fc=%d]", \ + (addr), (header)->nAllocLen, (header)->pBuffer, (fenceFd) +#define FULL_BUFFER(addr, header, fenceFd) "%#" PRIxPTR " [%u@%p (%u..+%u) f=%x ts=%lld fc=%d]", \ (intptr_t)(addr), (header)->nAllocLen, (header)->pBuffer, \ - (header)->nOffset, (header)->nFilledLen, (header)->nFlags, (header)->nTimeStamp + (header)->nOffset, (header)->nFilledLen, (header)->nFlags, (header)->nTimeStamp, (fenceFd) #define WITH_STATS_WRAPPER(fmt, ...) fmt " { IN=%zu/%zu OUT=%zu/%zu }", ##__VA_ARGS__, \ mInputBuffersWithCodec.size(), mNumPortBuffers[kPortIndexInput], \ @@ -1050,7 +1050,7 @@ status_t OMXNodeInstance::freeBuffer( return StatusFromOMXError(err); } -status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { +status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer, int fenceFd) { Mutex::Autolock autoLock(mLock); OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer); @@ -1058,15 +1058,22 @@ status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { header->nOffset = 0; header->nFlags = 0; + // meta now owns fenceFd + status_t res = storeFenceInMeta_l(header, fenceFd, kPortIndexOutput); + if (res != OK) { + CLOG_ERROR(fillBuffer::storeFenceInMeta, res, EMPTY_BUFFER(buffer, header, fenceFd)); + return res; + } + { Mutex::Autolock _l(mDebugLock); mOutputBuffersWithCodec.add(header); - CLOG_BUMPED_BUFFER(fillBuffer, WITH_STATS(EMPTY_BUFFER(buffer, header))); + CLOG_BUMPED_BUFFER(fillBuffer, WITH_STATS(EMPTY_BUFFER(buffer, header, fenceFd))); } OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header); if (err != OMX_ErrorNone) { - CLOG_ERROR(fillBuffer, err, EMPTY_BUFFER(buffer, header)); + CLOG_ERROR(fillBuffer, err, EMPTY_BUFFER(buffer, header, fenceFd)); Mutex::Autolock _l(mDebugLock); mOutputBuffersWithCodec.remove(header); } @@ -1076,7 +1083,7 @@ status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) { status_t OMXNodeInstance::emptyBuffer( OMX::buffer_id buffer, OMX_U32 rangeOffset, OMX_U32 rangeLength, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { Mutex::Autolock autoLock(mLock); OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer); @@ -1084,6 +1091,9 @@ status_t OMXNodeInstance::emptyBuffer( // corner case: we permit rangeOffset == end-of-buffer with rangeLength == 0. if (rangeOffset > header->nAllocLen || rangeLength > header->nAllocLen - rangeOffset) { + if (fenceFd >= 0) { + ::close(fenceFd); + } return BAD_VALUE; } header->nFilledLen = rangeLength; @@ -1110,7 +1120,7 @@ status_t OMXNodeInstance::emptyBuffer( buffer_meta->CopyToOMX(header); } - return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer); + return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer, fenceFd); } // log queued buffer activity for the next few input and/or output frames @@ -1137,11 +1147,62 @@ void OMXNodeInstance::unbumpDebugLevel_l(size_t portIndex) { } } +status_t OMXNodeInstance::storeFenceInMeta_l( + OMX_BUFFERHEADERTYPE *header, int fenceFd, OMX_U32 portIndex) { + // propagate fence if component supports it; wait for it otherwise + OMX_U32 metaSize = portIndex == kPortIndexInput ? header->nFilledLen : header->nAllocLen; + if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer + && metaSize >= sizeof(VideoNativeMetadata)) { + VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)(header->pBuffer); + if (nativeMeta.nFenceFd >= 0) { + ALOGE("fence (%d) already exists in meta", nativeMeta.nFenceFd); + if (fenceFd >= 0) { + ::close(fenceFd); + } + return ALREADY_EXISTS; + } + nativeMeta.nFenceFd = fenceFd; + } else if (fenceFd >= 0) { + CLOG_BUFFER(storeFenceInMeta, "waiting for fence %d", fenceFd); + sp<Fence> fence = new Fence(fenceFd); + return fence->wait(IOMX::kFenceTimeoutMs); + } + return OK; +} + +int OMXNodeInstance::retrieveFenceFromMeta_l( + OMX_BUFFERHEADERTYPE *header, OMX_U32 portIndex) { + OMX_U32 metaSize = portIndex == kPortIndexInput ? header->nAllocLen : header->nFilledLen; + int fenceFd = -1; + if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer + && header->nAllocLen >= sizeof(VideoNativeMetadata)) { + VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)(header->pBuffer); + if (nativeMeta.eType == kMetadataBufferTypeANWBuffer) { + fenceFd = nativeMeta.nFenceFd; + nativeMeta.nFenceFd = -1; + } + if (metaSize < sizeof(nativeMeta) && fenceFd >= 0) { + CLOG_ERROR(foundFenceInEmptyMeta, BAD_VALUE, FULL_BUFFER( + NULL, header, nativeMeta.nFenceFd)); + fenceFd = -1; + } + } + return fenceFd; +} + status_t OMXNodeInstance::emptyBuffer_l( - OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr) { + OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp, + intptr_t debugAddr, int fenceFd) { header->nFlags = flags; header->nTimeStamp = timestamp; + status_t res = storeFenceInMeta_l(header, fenceFd, kPortIndexInput); + if (res != OK) { + CLOG_ERROR(emptyBuffer::storeFenceInMeta, res, WITH_STATS( + FULL_BUFFER(debugAddr, header, fenceFd))); + return res; + } + { Mutex::Autolock _l(mDebugLock); mInputBuffersWithCodec.add(header); @@ -1151,11 +1212,11 @@ status_t OMXNodeInstance::emptyBuffer_l( bumpDebugLevel_l(2 /* numInputBuffers */, 0 /* numOutputBuffers */); } - CLOG_BUMPED_BUFFER(emptyBuffer, WITH_STATS(FULL_BUFFER(debugAddr, header))); + CLOG_BUMPED_BUFFER(emptyBuffer, WITH_STATS(FULL_BUFFER(debugAddr, header, fenceFd))); } OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header); - CLOG_IF_ERROR(emptyBuffer, err, FULL_BUFFER(debugAddr, header)); + CLOG_IF_ERROR(emptyBuffer, err, FULL_BUFFER(debugAddr, header, fenceFd)); { Mutex::Autolock _l(mDebugLock); @@ -1172,18 +1233,19 @@ status_t OMXNodeInstance::emptyBuffer_l( // like emptyBuffer, but the data is already in header->pBuffer status_t OMXNodeInstance::emptyGraphicBuffer( OMX_BUFFERHEADERTYPE *header, const sp<GraphicBuffer> &graphicBuffer, - OMX_U32 flags, OMX_TICKS timestamp) { + OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { Mutex::Autolock autoLock(mLock); OMX::buffer_id buffer = findBufferID(header); status_t err = updateGraphicBufferInMeta_l(kPortIndexInput, graphicBuffer, buffer, header); if (err != OK) { - CLOG_ERROR(emptyGraphicBuffer, err, FULL_BUFFER((intptr_t)header->pBuffer, header)); + CLOG_ERROR(emptyGraphicBuffer, err, FULL_BUFFER( + (intptr_t)header->pBuffer, header, fenceFd)); return err; } header->nOffset = 0; header->nFilledLen = graphicBuffer == NULL ? 0 : header->nAllocLen; - return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer); + return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer, fenceFd); } status_t OMXNodeInstance::getExtensionIndex( @@ -1307,7 +1369,8 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { mOutputBuffersWithCodec.remove(buffer); CLOG_BUMPED_BUFFER( - FBD, WITH_STATS(FULL_BUFFER(msg.u.extended_buffer_data.buffer, buffer))); + FBD, WITH_STATS(FULL_BUFFER( + msg.u.extended_buffer_data.buffer, buffer, msg.fenceFd))); unbumpDebugLevel_l(kPortIndexOutput); } @@ -1335,7 +1398,7 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { mInputBuffersWithCodec.remove(buffer); CLOG_BUMPED_BUFFER( - EBD, WITH_STATS(EMPTY_BUFFER(msg.u.buffer_data.buffer, buffer))); + EBD, WITH_STATS(EMPTY_BUFFER(msg.u.buffer_data.buffer, buffer, msg.fenceFd))); } if (bufferSource != NULL) { @@ -1344,7 +1407,7 @@ void OMXNodeInstance::onMessage(const omx_message &msg) { // Don't dispatch a message back to ACodec, since it doesn't // know that anyone asked to have the buffer emptied and will // be very confused. - bufferSource->codecBufferEmptied(buffer); + bufferSource->codecBufferEmptied(buffer, msg.fenceFd); return; } } @@ -1439,8 +1502,9 @@ OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone( if (instance->mDying) { return OMX_ErrorNone; } + int fenceFd = instance->retrieveFenceFromMeta_l(pBuffer, kPortIndexOutput); return instance->owner()->OnEmptyBufferDone(instance->nodeID(), - instance->findBufferID(pBuffer), pBuffer); + instance->findBufferID(pBuffer), pBuffer, fenceFd); } // static @@ -1452,8 +1516,9 @@ OMX_ERRORTYPE OMXNodeInstance::OnFillBufferDone( if (instance->mDying) { return OMX_ErrorNone; } + int fenceFd = instance->retrieveFenceFromMeta_l(pBuffer, kPortIndexOutput); return instance->owner()->OnFillBufferDone(instance->nodeID(), - instance->findBufferID(pBuffer), pBuffer); + instance->findBufferID(pBuffer), pBuffer, fenceFd); } void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) { diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp index 5f80cbc..cd1ac36 100644 --- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp @@ -517,6 +517,16 @@ const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer( // TODO do we need to support other formats? srcStride *= 4; } + + if (nativeMeta.nFenceFd >= 0) { + sp<Fence> fence = new Fence(nativeMeta.nFenceFd); + nativeMeta.nFenceFd = -1; + status_t err = fence->wait(IOMX::kFenceTimeoutMs); + if (err != OK) { + ALOGE("Timed out waiting on input fence"); + return NULL; + } + } } else { // TODO: remove this part. Check if anyone uses this. |