diff options
author | Igor Murashkin <iam@google.com> | 2013-04-29 10:31:06 -0700 |
---|---|---|
committer | Igor Murashkin <iam@google.com> | 2013-04-29 10:54:40 -0700 |
commit | d4d5227521a53dec6f77bed33846b4ccd4a760e4 (patch) | |
tree | 2e617eb08a2b78334566bcef9777f07340ad1bd1 /services/camera/libcameraservice/gui | |
parent | 5a269fa72b419e7fe4bf6bf9b27eec8782b3a963 (diff) | |
download | frameworks_av-d4d5227521a53dec6f77bed33846b4ccd4a760e4.zip frameworks_av-d4d5227521a53dec6f77bed33846b4ccd4a760e4.tar.gz frameworks_av-d4d5227521a53dec6f77bed33846b4ccd4a760e4.tar.bz2 |
camera: Add new RingBufferConsumer to keep a ring buffer of acquired frames
Bug: 8563838
Change-Id: I5a95e0be94e5388b30639905efae42d3c3279f72
Diffstat (limited to 'services/camera/libcameraservice/gui')
-rw-r--r-- | services/camera/libcameraservice/gui/RingBufferConsumer.cpp | 346 | ||||
-rw-r--r-- | services/camera/libcameraservice/gui/RingBufferConsumer.h | 189 |
2 files changed, 535 insertions, 0 deletions
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp new file mode 100644 index 0000000..1b2a717 --- /dev/null +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2013 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. + */ + +#undef NDEBUG +#include <cassert> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "RingBufferConsumer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Log.h> + +#include <gui/RingBufferConsumer.h> + +#define BI_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BI_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BI_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BI_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BI_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +typedef android::RingBufferConsumer::PinnedBufferItem PinnedBufferItem; + +namespace android { + +RingBufferConsumer::RingBufferConsumer(uint32_t consumerUsage, + int bufferCount) : + ConsumerBase(new BufferQueue(true)), + mBufferCount(bufferCount) +{ + mBufferQueue->setConsumerUsageBits(consumerUsage); + mBufferQueue->setSynchronousMode(true); + mBufferQueue->setMaxAcquiredBufferCount(bufferCount); + + assert(bufferCount > 0); +} + +RingBufferConsumer::~RingBufferConsumer() { +} + +void RingBufferConsumer::setName(const String8& name) { + Mutex::Autolock _l(mMutex); + mName = name; + mBufferQueue->setConsumerName(name); +} + +sp<PinnedBufferItem> RingBufferConsumer::pinSelectedBuffer( + const RingBufferComparator& filter, + bool waitForFence) { + + sp<PinnedBufferItem> pinnedBuffer; + + { + List<RingBufferItem>::iterator it, end, accIt; + BufferInfo acc, cur; + BufferInfo* accPtr = NULL; + + Mutex::Autolock _l(mMutex); + + for (it = mBufferItemList.begin(), end = mBufferItemList.end(); + it != end; + ++it) { + + const RingBufferItem& item = *it; + + cur.mCrop = item.mCrop; + cur.mTransform = item.mTransform; + cur.mScalingMode = item.mScalingMode; + cur.mTimestamp = item.mTimestamp; + cur.mFrameNumber = item.mFrameNumber; + cur.mPinned = item.mPinCount > 0; + + int ret = filter.compare(accPtr, &cur); + + if (ret == 0) { + accPtr = NULL; + } else if (ret > 0) { + acc = cur; + accPtr = &acc; + accIt = it; + } // else acc = acc + } + + if (!accPtr) { + return NULL; + } + + pinnedBuffer = new PinnedBufferItem(this, *accIt); + pinBufferLocked(pinnedBuffer->getBufferItem()); + + } // end scope of mMutex autolock + + if (pinnedBuffer != 0) { + BI_LOGV("Pinned buffer frame %lld, timestamp %lld", + pinnedBuffer->getBufferItem().mFrameNumber, + pinnedBuffer->getBufferItem().mTimestamp); + } + + if (waitForFence) { + status_t err = pinnedBuffer->getBufferItem().mFence->waitForever(1000, + "RingBufferConsumer::pinSelectedBuffer"); + if (err != OK) { + BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", + strerror(-err), err); + } + } + + return pinnedBuffer; +} + +status_t RingBufferConsumer::clear() { + + status_t err; + Mutex::Autolock _l(mMutex); + + BI_LOGV("%s", __FUNCTION__); + + // Avoid annoying log warnings by returning early + if (mBufferItemList.size() == 0) { + return OK; + } + + do { + size_t pinnedFrames = 0; + err = releaseOldestBufferLocked(&pinnedFrames); + + if (err == NO_BUFFER_AVAILABLE) { + assert(pinnedFrames == mBufferItemList.size()); + break; + } + + if (err == NOT_ENOUGH_DATA) { + // Fine. Empty buffer item list. + break; + } + + if (err != OK) { + BI_LOGE("Clear failed, could not release buffer"); + return err; + } + + } while(true); + + return OK; +} + +void RingBufferConsumer::pinBufferLocked(const BufferItem& item) { + List<RingBufferItem>::iterator it, end; + + for (it = mBufferItemList.begin(), end = mBufferItemList.end(); + it != end; + ++it) { + + RingBufferItem& find = *it; + if (item.mGraphicBuffer == find.mGraphicBuffer) { + find.mPinCount++; + break; + } + } + + if (it == end) { + BI_LOGE("Failed to pin buffer (timestamp %lld, framenumber %lld)", + item.mTimestamp, item.mFrameNumber); + } +} + +status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) { + status_t err = OK; + + List<RingBufferItem>::iterator it, end, accIt; + + it = mBufferItemList.begin(); + end = mBufferItemList.end(); + accIt = it; + + if (it == end) { + /** + * This is fine. We really care about being able to acquire a buffer + * successfully after this function completes, not about it releasing + * some buffer. + */ + BI_LOGV("%s: No buffers yet acquired, can't release anything", + __FUNCTION__); + return NOT_ENOUGH_DATA; + } + + for (; it != end; ++it) { + RingBufferItem& find = *it; + if (find.mTimestamp < accIt->mTimestamp && find.mPinCount <= 0) { + accIt = it; + } + + if (find.mPinCount > 0 && pinnedFrames != NULL) { + ++(*pinnedFrames); + } + } + + if (accIt != end) { + RingBufferItem& item = *accIt; + + // In case the object was never pinned, pass the acquire fence + // back to the release fence. If the fence was already waited on, + // it'll just be a no-op to wait on it again. + err = addReleaseFenceLocked(item.mBuf, item.mFence); + + if (err != OK) { + BI_LOGE("Failed to add release fence to buffer " + "(timestamp %lld, framenumber %lld", + item.mTimestamp, item.mFrameNumber); + return err; + } + + BI_LOGV("Attempting to release buffer timestamp %lld, frame %lld", + item.mTimestamp, item.mFrameNumber); + + err = releaseBufferLocked(item.mBuf, + EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + if (err != OK) { + BI_LOGE("Failed to release buffer: %s (%d)", + strerror(-err), err); + return err; + } + + BI_LOGV("Buffer timestamp %lld, frame %lld evicted", + item.mTimestamp, item.mFrameNumber); + + size_t currentSize = mBufferItemList.size(); + mBufferItemList.erase(accIt); + assert(mBufferItemList.size() == currentSize - 1); + } else { + BI_LOGW("All buffers pinned, could not find any to release"); + return NO_BUFFER_AVAILABLE; + + } + + return OK; +} + +void RingBufferConsumer::onFrameAvailable() { + status_t err; + + { + Mutex::Autolock _l(mMutex); + + /** + * Release oldest frame + */ + if (mBufferItemList.size() >= (size_t)mBufferCount) { + err = releaseOldestBufferLocked(/*pinnedFrames*/NULL); + assert(err != NOT_ENOUGH_DATA); + + // TODO: implement the case for NO_BUFFER_AVAILABLE + assert(err != NO_BUFFER_AVAILABLE); + if (err != OK) { + return; + } + // TODO: in unpinBuffer rerun this routine if we had buffers + // we could've locked but didn't because there was no space + } + + RingBufferItem& item = *mBufferItemList.insert(mBufferItemList.end(), + RingBufferItem()); + + /** + * Acquire new frame + */ + err = acquireBufferLocked(&item); + if (err != OK) { + if (err != NO_BUFFER_AVAILABLE) { + BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + } + + mBufferItemList.erase(--mBufferItemList.end()); + return; + } + + BI_LOGV("New buffer acquired (timestamp %lld), " + "buffer items %u out of %d", + item.mTimestamp, + mBufferItemList.size(), mBufferCount); + + item.mGraphicBuffer = mSlots[item.mBuf].mGraphicBuffer; + } // end of mMutex lock + + ConsumerBase::onFrameAvailable(); +} + +void RingBufferConsumer::unpinBuffer(const BufferItem& item) { + Mutex::Autolock _l(mMutex); + + List<RingBufferItem>::iterator it, end, accIt; + + for (it = mBufferItemList.begin(), end = mBufferItemList.end(); + it != end; + ++it) { + + RingBufferItem& find = *it; + if (item.mGraphicBuffer == find.mGraphicBuffer) { + status_t res = addReleaseFenceLocked(item.mBuf, item.mFence); + + if (res != OK) { + BI_LOGE("Failed to add release fence to buffer " + "(timestamp %lld, framenumber %lld", + item.mTimestamp, item.mFrameNumber); + return; + } + + find.mPinCount--; + break; + } + } + + if (it == end) { + BI_LOGE("Failed to unpin buffer (timestamp %lld, framenumber %lld", + item.mTimestamp, item.mFrameNumber); + } +} + +status_t RingBufferConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock _l(mMutex); + return mBufferQueue->setDefaultBufferSize(w, h); +} + +status_t RingBufferConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { + Mutex::Autolock _l(mMutex); + return mBufferQueue->setDefaultBufferFormat(defaultFormat); +} + +status_t RingBufferConsumer::setConsumerUsage(uint32_t usage) { + Mutex::Autolock _l(mMutex); + return mBufferQueue->setConsumerUsageBits(usage); +} + +} // namespace android diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h new file mode 100644 index 0000000..454fbae --- /dev/null +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef ANDROID_GUI_RINGBUFFERCONSUMER_H +#define ANDROID_GUI_RINGBUFFERCONSUMER_H + +#include <gui/ConsumerBase.h> + +#include <ui/GraphicBuffer.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/threads.h> +#include <utils/List.h> + +#define ANDROID_GRAPHICS_RINGBUFFERCONSUMER_JNI_ID "mRingBufferConsumer" + +namespace android { + +/** + * The RingBufferConsumer maintains a ring buffer of BufferItem objects, + * (which are 'acquired' as long as they are part of the ring buffer, and + * 'released' when they leave the ring buffer). + * + * When new buffers are produced, the oldest non-pinned buffer item is immediately + * dropped from the ring buffer, and overridden with the newest buffer. + * + * Users can only access a buffer item after pinning it (which also guarantees + * that during its duration it will not be released back into the BufferQueue). + * + * Note that the 'oldest' buffer is the one with the smallest timestamp. + * + * Edge cases: + * - If ringbuffer is not full, no drops occur when a buffer is produced. + * - If all the buffers get filled or pinned then there will be no empty + * buffers left, so the producer will block on dequeue. + */ +class RingBufferConsumer : public ConsumerBase, + public ConsumerBase::FrameAvailableListener +{ + public: + typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; + + typedef BufferQueue::BufferItem BufferItem; + + enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT }; + enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; + + // Create a new ring buffer consumer. The consumerUsage parameter determines + // the consumer usage flags passed to the graphics allocator. The + // bufferCount parameter specifies how many buffers can be pinned for user + // access at the same time. + RingBufferConsumer(uint32_t consumerUsage, + int bufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS); + + virtual ~RingBufferConsumer(); + + // set the name of the RingBufferConsumer that will be used to identify it in + // log messages. + void setName(const String8& name); + + sp<IGraphicBufferProducer> getProducerInterface() const { return getBufferQueue(); } + + // setDefaultBufferSize is used to set the size of buffers returned by + // requestBuffers when a with and height of zero is requested. + status_t setDefaultBufferSize(uint32_t w, uint32_t h); + + // setDefaultBufferFormat allows the BufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // by the producer endpoint. + status_t setDefaultBufferFormat(uint32_t defaultFormat); + + // setConsumerUsage allows the BufferQueue consumer usage to be + // set at a later time after construction. + status_t setConsumerUsage(uint32_t usage); + + // Buffer info, minus the graphics buffer/slot itself. + struct BufferInfo { + // mCrop is the current crop rectangle for this buffer slot. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mPinned is whether or not the buffer has been pinned already. + bool mPinned; + }; + + struct RingBufferComparator { + // Return < 0 to select i1, > 0 to select i2, 0 for neither + // i1 or i2 can be NULL. + // + // The comparator has to implement a total ordering. Otherwise + // a linear scan won't find the most preferred buffer. + virtual int compare(const BufferInfo* i1, + const BufferInfo* i2) const = 0; + + virtual ~RingBufferComparator() {} + }; + + struct PinnedBufferItem : public LightRefBase<PinnedBufferItem> { + PinnedBufferItem(wp<RingBufferConsumer> consumer, + const BufferItem& item) : + mConsumer(consumer), + mBufferItem(item) { + } + + ~PinnedBufferItem() { + sp<RingBufferConsumer> consumer = mConsumer.promote(); + if (consumer != NULL) { + consumer->unpinBuffer(mBufferItem); + } + } + + bool isEmpty() { + return mBufferItem.mBuf == BufferQueue::INVALID_BUFFER_SLOT; + } + + BufferItem& getBufferItem() { return mBufferItem; } + const BufferItem& getBufferItem() const { return mBufferItem; } + + private: + wp<RingBufferConsumer> mConsumer; + BufferItem mBufferItem; + }; + + // Find a buffer using the filter, then pin it before returning it. + // + // The filter will be invoked on each buffer item in the ring buffer, + // passing the item that was selected from each previous iteration, + // as well as the current iteration's item. + // + // Pinning will ensure that the buffer will not be dropped when a new + // frame is available. + sp<PinnedBufferItem> pinSelectedBuffer(const RingBufferComparator& filter, + bool waitForFence = true); + + // Release all the non-pinned buffers in the ring buffer + status_t clear(); + + private: + + // Override ConsumerBase::onFrameAvailable + virtual void onFrameAvailable(); + + void pinBufferLocked(const BufferItem& item); + void unpinBuffer(const BufferItem& item); + + // Releases oldest buffer. Returns NO_BUFFER_AVAILABLE + // if all the buffers were pinned. + // Returns NOT_ENOUGH_DATA if list was empty. + status_t releaseOldestBufferLocked(size_t* pinnedFrames); + + struct RingBufferItem : public BufferItem { + RingBufferItem() : BufferItem(), mPinCount(0) {} + int mPinCount; + }; + + // List of acquired buffers in our ring buffer + List<RingBufferItem> mBufferItemList; + const int mBufferCount; +}; + +} // namespace android + +#endif // ANDROID_GUI_CPUCONSUMER_H |