Camera3: add camera3 buffer manager support
* Add camera buffer manager for buffer allocation and sharing management across
multiple streams. Only gralloc v0 implementation is done, v1 implementation is
pending. With this, the max mem footprint for multiple streams in the same
stream set will be the max buffer count x max buffer size.
* API1 client will still use the old bufferQueue code path, buffer manager
is only targeting at API2 clients.
* Prepare and teardown should work with buffer manager.
* Some existing code typo fix and cleanup (to fix the compiling warnings).
Bug: 25088440
Change-Id: I68b246faa43080302acd02a8e976384bd3e26a23
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 9ba8f3f..d416353 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -46,6 +46,7 @@
device3/Camera3ZslStream.cpp \
device3/Camera3DummyStream.cpp \
device3/StatusTracker.cpp \
+ device3/Camera3BufferManager.cpp \
gui/RingBufferConsumer.cpp \
utils/CameraTraces.cpp \
utils/AutoConditionLock.cpp
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index bd9fea3..240a33f 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -456,7 +456,8 @@
return BAD_VALUE;
}
- int streamId = -1;
+ // TODO: Hookup the stream set id with upper layer.
+ int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
res = mDevice->createStream(surface, width, height, format, dataSpace,
static_cast<camera3_stream_rotation_t>
(outputConfiguration.getRotation()),
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 7b083a3..6fd2b39 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -31,6 +31,7 @@
#include "camera/CaptureResult.h"
#include "common/CameraModule.h"
#include "gui/IGraphicBufferProducer.h"
+#include "device3/Camera3StreamInterface.h"
namespace android {
@@ -108,7 +109,8 @@
*/
virtual status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id) = 0;
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID) = 0;
/**
* Create an input stream of width, height, and format.
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
new file mode 100644
index 0000000..b2a3076
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2016 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 "Camera3-BufferManager"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+
+#include <gui/ISurfaceComposer.h>
+#include <private/gui/ComposerService.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include "utils/CameraTraces.h"
+#include "Camera3BufferManager.h"
+
+namespace android {
+
+namespace camera3 {
+
+Camera3BufferManager::Camera3BufferManager(const sp<IGraphicBufferAlloc>& allocator) :
+ mAllocator(allocator) {
+ if (allocator == NULL) {
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ mAllocator = composer->createGraphicBufferAlloc();
+ if (mAllocator == NULL) {
+ ALOGE("createGraphicBufferAlloc failed");
+ }
+ }
+}
+
+Camera3BufferManager::~Camera3BufferManager() {
+}
+
+status_t Camera3BufferManager::registerStream(const StreamInfo& streamInfo) {
+ ATRACE_CALL();
+
+ int streamId = streamInfo.streamId;
+ int streamSetId = streamInfo.streamSetId;
+
+ if (streamId == CAMERA3_STREAM_ID_INVALID || streamSetId == CAMERA3_STREAM_SET_ID_INVALID) {
+ ALOGE("%s: Stream id (%d) or stream set id (%d) is invalid",
+ __FUNCTION__, streamId, streamSetId);
+ return BAD_VALUE;
+ }
+ if (streamInfo.totalBufferCount > kMaxBufferCount || streamInfo.totalBufferCount == 0) {
+ ALOGE("%s: Stream id (%d) with stream set id (%d) total buffer count %zu is invalid",
+ __FUNCTION__, streamId, streamSetId, streamInfo.totalBufferCount);
+ return BAD_VALUE;
+ }
+ if (!streamInfo.isConfigured) {
+ ALOGE("%s: Stream (%d) is not configured", __FUNCTION__, streamId);
+ return BAD_VALUE;
+ }
+
+ // For Gralloc v1, try to allocate a buffer and see if it is successful, otherwise, stream
+ // buffer sharing for this newly added stream is not supported. For Gralloc v0, we don't
+ // need check this, as the buffers are not really shared between streams, the buffers are
+ // allocated for each stream individually, the allocation failure will be checked in
+ // getBufferForStream() call.
+ if (mGrallocVersion > HARDWARE_DEVICE_API_VERSION(0,1)) {
+ // TODO: To be implemented.
+
+ // In case allocation fails, return invalid operation
+ return INVALID_OPERATION;
+ }
+
+ Mutex::Autolock l(mLock);
+ if (mAllocator == NULL) {
+ ALOGE("%s: allocator is NULL, buffer manager is bad state.", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ // Check if this stream was registered with different stream set ID, if so, error out.
+ for (size_t i = 0; i < mStreamSetMap.size(); i++) {
+ ssize_t streamIdx = mStreamSetMap[i].streamInfoMap.indexOfKey(streamId);
+ if (streamIdx != NAME_NOT_FOUND &&
+ mStreamSetMap[i].streamInfoMap[streamIdx].streamSetId != streamInfo.streamSetId) {
+ ALOGE("%s: It is illegal to register the same stream id with different stream set",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ }
+ // Check if there is an existing stream set registered; if not, create one; otherwise, add this
+ // stream info to the existing stream set entry.
+ ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId);
+ if (setIdx == NAME_NOT_FOUND) {
+ ALOGV("%s: stream set %d is not registered to stream set map yet, create it.",
+ __FUNCTION__, streamSetId);
+ // Create stream info map, then add to mStreamsetMap.
+ StreamSet newStreamSet;
+ setIdx = mStreamSetMap.add(streamSetId, newStreamSet);
+ }
+ // Update stream set map and water mark.
+ StreamSet& currentStreamSet = mStreamSetMap.editValueAt(setIdx);
+ ssize_t streamIdx = currentStreamSet.streamInfoMap.indexOfKey(streamId);
+ if (streamIdx != NAME_NOT_FOUND) {
+ ALOGW("%s: stream %d was already registered with stream set %d",
+ __FUNCTION__, streamId, streamSetId);
+ return OK;
+ }
+ currentStreamSet.streamInfoMap.add(streamId, streamInfo);
+ currentStreamSet.handoutBufferCountMap.add(streamId, 0);
+
+ // The watermark should be the max of buffer count of each stream inside a stream set.
+ if (streamInfo.totalBufferCount > currentStreamSet.allocatedBufferWaterMark) {
+ currentStreamSet.allocatedBufferWaterMark = streamInfo.totalBufferCount;
+ }
+
+ return OK;
+}
+
+status_t Camera3BufferManager::unregisterStream(int streamId, int streamSetId) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+ ALOGV("%s: unregister stream %d with stream set %d", __FUNCTION__,
+ streamId, streamSetId);
+ if (mAllocator == NULL) {
+ ALOGE("%s: allocator is NULL, buffer manager is bad state.", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
+ ALOGE("%s: stream %d with set id %d wasn't properly registered to this buffer manager!",
+ __FUNCTION__, streamId, streamSetId);
+ return BAD_VALUE;
+ }
+
+ // De-list all the buffers associated with this stream first.
+ StreamSet& currentSet = mStreamSetMap.editValueFor(streamSetId);
+ BufferList& freeBufs = currentSet.freeBuffers;
+ BufferCountMap& handOutBufferCounts = currentSet.handoutBufferCountMap;
+ InfoMap& infoMap = currentSet.streamInfoMap;
+ removeBuffersFromBufferListLocked(freeBufs, streamId);
+ handOutBufferCounts.removeItem(streamId);
+
+ // Remove the stream info from info map and recalculate the buffer count water mark.
+ infoMap.removeItem(streamId);
+ currentSet.allocatedBufferWaterMark = 0;
+ for (size_t i = 0; i < infoMap.size(); i++) {
+ if (infoMap[i].totalBufferCount > currentSet.allocatedBufferWaterMark) {
+ currentSet.allocatedBufferWaterMark = infoMap[i].totalBufferCount;
+ }
+ }
+
+ // Remove this stream set if all its streams have been removed.
+ if (freeBufs.size() == 0 && handOutBufferCounts.size() == 0 && infoMap.size() == 0) {
+ mStreamSetMap.removeItem(streamSetId);
+ }
+
+ return OK;
+}
+
+status_t Camera3BufferManager::getBufferForStream(int streamId, int streamSetId,
+ sp<GraphicBuffer>* gb, int* fenceFd) {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+ ALOGV("%s: get buffer for stream %d with stream set %d", __FUNCTION__,
+ streamId, streamSetId);
+ if (mAllocator == NULL) {
+ ALOGE("%s: allocator is NULL, buffer manager is bad state.", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetId)) {
+ ALOGE("%s: stream %d is not registered with stream set %d yet!!!",
+ __FUNCTION__, streamId, streamSetId);
+ return BAD_VALUE;
+ }
+
+ StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+ GraphicBufferEntry buffer =
+ getFirstBufferFromBufferListLocked(streamSet.freeBuffers, streamId);
+
+ if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
+ // Allocate one if there is no free buffer available.
+ if (buffer.graphicBuffer == nullptr) {
+ const StreamInfo& info = streamSet.streamInfoMap.valueFor(streamId);
+ status_t res = OK;
+ buffer.fenceFd = -1;
+ buffer.graphicBuffer = mAllocator->createGraphicBuffer(
+ info.width, info.height, info.format, info.combinedUsage, &res);
+ ALOGV("%s: allocate a new graphic buffer %p with handle %p",
+ __FUNCTION__, buffer.graphicBuffer.get(), buffer.graphicBuffer->handle);
+ if (res != OK) {
+ ALOGE("%s: graphic buffer allocation failed: (error %d %s) ",
+ __FUNCTION__, res, strerror(-res));
+ return res;
+ }
+ }
+
+ // Increase the hand-out buffer count for tracking purpose.
+ BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
+ size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
+ bufferCount++;
+
+ *gb = buffer.graphicBuffer;
+ *fenceFd = buffer.fenceFd;
+ ALOGV("%s: get buffer (%p) with handle (%p).",
+ __FUNCTION__, buffer.graphicBuffer.get(), buffer.graphicBuffer->handle);
+
+ // Proactively free buffers for other streams if the current number of allocated buffers
+ // exceeds the water mark. This only for Gralloc V1, for V2, this logic can also be handled
+ // in returnBufferForStream() if we want to free buffer more quickly.
+ // TODO: probably should find out all the inactive stream IDs, and free the firstly found
+ // buffers for them.
+ StreamId firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
+ if (streamSet.streamInfoMap.size() > 1) {
+ for (size_t i = 0; i < streamSet.streamInfoMap.size(); i++) {
+ firstOtherStreamId = streamSet.streamInfoMap[i].streamId;
+ if (firstOtherStreamId != streamId) {
+ break;
+ }
+ }
+ if (firstOtherStreamId == CAMERA3_STREAM_ID_INVALID) {
+ return OK;
+ }
+
+ // This will drop the reference to one free buffer, which will effectively free one
+ // buffer (from the free buffer list) for the inactive streams.
+ size_t totalAllocatedBufferCount = streamSet.freeBuffers.size();
+ for (size_t i = 0; i < streamSet.handoutBufferCountMap.size(); i++) {
+ totalAllocatedBufferCount += streamSet.handoutBufferCountMap[i];
+ }
+ if (totalAllocatedBufferCount > streamSet.allocatedBufferWaterMark) {
+ getFirstBufferFromBufferListLocked(streamSet.freeBuffers, firstOtherStreamId);
+ }
+ }
+ } else {
+ // TODO: implement this.
+ return BAD_VALUE;
+ }
+
+ return OK;
+}
+
+status_t Camera3BufferManager::returnBufferForStream(int streamId,
+ int streamSetId, const sp<GraphicBuffer>& buffer, int fenceFd) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+ ALOGV_IF(buffer != 0, "%s: return buffer (%p) with handle (%p) for stream %d and stream set %d",
+ __FUNCTION__, buffer.get(), buffer->handle, streamId, streamSetId);
+ if (mAllocator == NULL) {
+ ALOGE("%s: allocator is NULL, buffer manager is bad state.", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
+ ALOGV("%s: returning buffer for an already unregistered stream (stream %d with set id %d),"
+ "buffer will be dropped right away!", __FUNCTION__, streamId, streamSetId);
+ return OK;
+ }
+
+ if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
+ // Add to the freeBuffer list.
+ StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetId);
+ if (buffer != 0) {
+ BufferEntry entry;
+ entry.add(streamId, GraphicBufferEntry(buffer, fenceFd));
+ status_t res = addBufferToBufferListLocked(streamSet.freeBuffers, entry);
+ if (res != OK) {
+ ALOGE("%s: add buffer to free buffer list failed", __FUNCTION__);
+ return res;
+ }
+ }
+
+ // Update the hand-out buffer count for this buffer.
+ BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
+ size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
+ bufferCount--;
+ } else {
+ // TODO: implement this.
+ return BAD_VALUE;
+ }
+
+ return OK;
+}
+
+void Camera3BufferManager::dump(int fd, const Vector<String16>& args) const {
+ Mutex::Autolock l(mLock);
+
+ (void) args;
+ String8 lines;
+ lines.appendFormat(" Total stream sets: %zu\n", mStreamSetMap.size());
+ for (size_t i = 0; i < mStreamSetMap.size(); i++) {
+ lines.appendFormat(" Stream set %d has below streams:\n", mStreamSetMap.keyAt(i));
+ for (size_t j = 0; j < mStreamSetMap[i].streamInfoMap.size(); j++) {
+ lines.appendFormat(" Stream %d\n", mStreamSetMap[i].streamInfoMap[j].streamId);
+ }
+ lines.appendFormat(" Stream set buffer count water mark: %zu\n",
+ mStreamSetMap[i].allocatedBufferWaterMark);
+ lines.appendFormat(" Handout buffer counts:\n");
+ for (size_t m = 0; m < mStreamSetMap[i].handoutBufferCountMap.size(); m++) {
+ int streamId = mStreamSetMap[i].handoutBufferCountMap.keyAt(m);
+ size_t bufferCount = mStreamSetMap[i].handoutBufferCountMap.valueAt(m);
+ lines.appendFormat(" stream id: %d, buffer count: %zu.\n",
+ streamId, bufferCount);
+ }
+
+ lines.appendFormat(" Free buffer count: %zu\n",
+ mStreamSetMap[i].freeBuffers.size());
+ for (auto& bufEntry : mStreamSetMap[i].freeBuffers) {
+ for (size_t m = 0; m < bufEntry.size(); m++) {
+ const sp<GraphicBuffer>& buffer = bufEntry.valueAt(m).graphicBuffer;
+ int streamId = bufEntry.keyAt(m);
+ lines.appendFormat(" stream id: %d, buffer: %p, handle: %p.\n",
+ streamId, buffer.get(), buffer->handle);
+ }
+ }
+ }
+ write(fd, lines.string(), lines.size());
+}
+
+bool Camera3BufferManager::checkIfStreamRegisteredLocked(int streamId, int streamSetId) const {
+ ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId);
+ if (setIdx == NAME_NOT_FOUND) {
+ ALOGV("%s: stream set %d is not registered to stream set map yet!",
+ __FUNCTION__, streamSetId);
+ return false;
+ }
+
+ ssize_t streamIdx = mStreamSetMap.valueAt(setIdx).streamInfoMap.indexOfKey(streamId);
+ if (streamIdx == NAME_NOT_FOUND) {
+ ALOGV("%s: stream %d is not registered to stream info map yet!", __FUNCTION__, streamId);
+ return false;
+ }
+
+ size_t bufferWaterMark = mStreamSetMap[setIdx].allocatedBufferWaterMark;
+ if (bufferWaterMark == 0 || bufferWaterMark > kMaxBufferCount) {
+ ALOGW("%s: stream %d with stream set %d is not registered correctly to stream set map,"
+ " as the water mark (%zu) is wrong!",
+ __FUNCTION__, streamId, streamSetId, bufferWaterMark);
+ return false;
+ }
+
+ return true;
+}
+
+status_t Camera3BufferManager::addBufferToBufferListLocked(BufferList& bufList,
+ const BufferEntry& buffer) {
+ // TODO: need add some sanity check here.
+ bufList.push_back(buffer);
+
+ return OK;
+}
+
+status_t Camera3BufferManager::removeBuffersFromBufferListLocked(BufferList& bufferList,
+ int streamId) {
+ BufferList::iterator i = bufferList.begin();
+ while (i != bufferList.end()) {
+ ssize_t idx = i->indexOfKey(streamId);
+ if (idx != NAME_NOT_FOUND) {
+ i->removeItem(streamId);
+ if (i->isEmpty()) {
+ i = bufferList.erase(i);
+ }
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ ALOGW_IF(i == bufferList.end(), "%s: Unable to find buffers for stream %d",
+ __FUNCTION__, streamId);
+
+ return OK;
+}
+
+Camera3BufferManager::GraphicBufferEntry Camera3BufferManager::getFirstBufferFromBufferListLocked(
+ BufferList& buffers, int streamId) {
+ // Try to get the first buffer from the free buffer list if there is one.
+ GraphicBufferEntry entry;
+ BufferList::iterator i = buffers.begin();
+ while (i != buffers.end()) {
+ ssize_t idx = i->indexOfKey(streamId);
+ if (idx != NAME_NOT_FOUND) {
+ entry = GraphicBufferEntry(i->valueAt(idx));
+ i = buffers.erase(i);
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ ALOGV_IF(entry.graphicBuffer == 0, "%s: Unable to find free buffer for stream %d",
+ __FUNCTION__, streamId);
+ return entry;
+}
+
+} // namespace camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.h b/services/camera/libcameraservice/device3/Camera3BufferManager.h
new file mode 100644
index 0000000..0b4f55c
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.h
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2016 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_SERVERS_CAMERA3_BUFFER_MANAGER_H
+#define ANDROID_SERVERS_CAMERA3_BUFFER_MANAGER_H
+
+#include <list>
+#include <algorithm>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include "Camera3OutputStream.h"
+
+namespace android {
+
+namespace camera3 {
+
+struct StreamInfo;
+
+/**
+ * A class managing the graphic buffers that is used by camera output streams. It allocates and
+ * hands out Gralloc buffers to the clients (e.g., Camera3OutputStream) based on the requests.
+ * When clients request a buffer, buffer manager will pick a buffer if there are some already
+ * allocated buffer available, will allocate a buffer otherwise. When there are too many allocated
+ * buffer maintained by the buffer manager, it will dynamically deallocate some buffers that are
+ * solely owned by this buffer manager.
+ * In doing so, it reduces the memory footprint unless it is already minimal without impacting
+ * performance.
+ *
+ */
+class Camera3BufferManager: public virtual RefBase {
+public:
+ Camera3BufferManager(const sp<IGraphicBufferAlloc>& allocator = NULL);
+
+ virtual ~Camera3BufferManager();
+
+ /**
+ * This method registers an output stream to this buffer manager by using the provided stream
+ * information.
+ *
+ * The stream info includes the necessary information such as stream size, format, buffer count,
+ * usage flags, etc. for the buffer manager to allocate and hand out buffers for this stream.
+ *
+ * It's illegal to call this method if the stream is not CONFIGURED yet, as some critical
+ * stream properties (e.g., combined usage flags) are only available in this state. It is also
+ * illegal to call this method with an invalid stream set ID (CAMERA3_STREAM_SET_ID_INVALID),
+ * as the invalid stream set ID indicates that this stream doesn't intend to use buffer manager.
+ *
+ *
+ * Once a stream is successfully registered to this buffer manager, the buffer manager takes
+ * over the buffer allocation role and provides buffers to this stream via getBufferForStream().
+ * The returned buffer can be sent to the camera HAL for image output, and then queued to the
+ * ANativeWindow (Surface) for downstream consumer to acquire. Once the image buffer is released
+ * by the consumer end point, the BufferQueueProducer callback onBufferReleased will call
+ * returnBufferForStream() to return the free buffer to this buffer manager. If the stream
+ * uses buffer manager to manage the stream buffers, it should disable the BufferQueue
+ * allocation via IGraphicBufferProducer::allowAllocation(false).
+ *
+ * Registering an already registered stream has no effect.
+ *
+ * Return values:
+ *
+ * OK: Registration of the new stream was successful.
+ * BAD_VALUE: This stream is not at CONFIGURED state, or the stream ID or stream set
+ * ID are invalid, or attempting to register the same stream to multiple
+ * stream sets, or other stream properties are invalid.
+ * INVALID_OPERATION: This buffer manager doesn't support buffer sharing across this stream
+ * and other streams that were already registered with the same stream set
+ * ID.
+ */
+ status_t registerStream(const StreamInfo &streamInfo);
+
+ /**
+ * This method unregisters a stream from this buffer manager.
+ *
+ * After a stream is unregistered, further getBufferForStream() calls will fail for this stream.
+ * After all streams for a given stream set are unregistered, all the buffers solely owned (for
+ * this stream set) by this buffer manager will be freed; all buffers subsequently returned to
+ * this buffer manager for this stream set will be freed immediately.
+ *
+ * Return values:
+ *
+ * OK: Removal of the a stream from this buffer manager was successful.
+ * BAD_VALUE: stream ID or stream set ID are invalid, or stream ID and stream set ID
+ * combination doesn't match what was registered, or this stream wasn't registered
+ * to this buffer manager before.
+ */
+ status_t unregisterStream(int streamId, int streamSetId);
+
+ /**
+ * This method obtains a buffer for a stream from this buffer manager.
+ *
+ * This method returns the first free buffer from the free buffer list (associated with this
+ * stream set) if there is any. Otherwise, it will allocate a buffer for this stream, return
+ * it and increment its count of handed-out buffers. When the total number of allocated buffers
+ * is too high, it may deallocate the unused buffers to save memory footprint of this stream
+ * set.
+ *
+ * After this call, the client takes over the ownership of this buffer if it is not freed.
+ *
+ * Return values:
+ *
+ * OK: Getting buffer for this stream was successful.
+ * BAD_VALUE: stream ID or streamSetId are invalid, or stream ID and stream set ID
+ * combination doesn't match what was registered, or this stream wasn't registered
+ * to this buffer manager before.
+ * NO_MEMORY: Unable to allocate a buffer for this stream at this time.
+ */
+ status_t getBufferForStream(int streamId, int streamSetId, sp<GraphicBuffer>* gb, int* fenceFd);
+
+ /**
+ * This method returns a buffer for a stream to this buffer manager.
+ *
+ * When a buffer is returned, it is treated as a free buffer and may either be reused for future
+ * getBufferForStream() calls, or freed if there total number of outstanding allocated buffers
+ * is too large. The latter only applies to the case where the buffer are physically shared
+ * between streams in the same stream set. A physically shared buffer is the buffer that has one
+ * physical back store but multiple handles. Multiple stream can access the same physical memory
+ * with their own handles. Physically shared buffer can only be supported by Gralloc HAL V1.
+ * See hardware/libhardware/include/hardware/gralloc1.h for more details.
+ *
+ *
+ * This call takes the ownership of the returned buffer if it was allocated by this buffer
+ * manager; clients should not use this buffer after this call. Attempting to access this buffer
+ * after this call will have undefined behavior. Holding a reference to this buffer after this
+ * call may cause memory leakage. If a BufferQueue is used to track the buffers handed out by
+ * this buffer queue, it is recommended to call detachNextBuffer() from the buffer queue after
+ * BufferQueueProducer onBufferReleased callback is fired, and return it to this buffer manager.
+ *
+ * OK: Buffer return for this stream was successful.
+ * BAD_VALUE: stream ID or streamSetId are invalid, or stream ID and stream set ID combination
+ * doesn't match what was registered, or this stream wasn't registered to this
+ * buffer manager before.
+ */
+ status_t returnBufferForStream(int streamId, int streamSetId, const sp<GraphicBuffer>& buffer,
+ int fenceFd);
+
+ /**
+ * Dump the buffer manager statistics.
+ */
+ void dump(int fd, const Vector<String16> &args) const;
+
+private:
+ /**
+ * Lock to synchronize the access to the methods of this class.
+ */
+ mutable Mutex mLock;
+
+ static const size_t kMaxBufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+ /**
+ * mAllocator is the connection to SurfaceFlinger that is used to allocate new GraphicBuffer
+ * objects.
+ */
+ sp<IGraphicBufferAlloc> mAllocator;
+
+ struct GraphicBufferEntry {
+ sp<GraphicBuffer> graphicBuffer;
+ int fenceFd;
+ GraphicBufferEntry(const sp<GraphicBuffer>& gb = 0, int fd = -1) :
+ graphicBuffer(gb),
+ fenceFd(fd) {}
+ };
+
+ /**
+ * A buffer entry (indexed by stream ID) represents a single physically allocated buffer. For
+ * Gralloc V0, since each physical buffer is associated with one stream, this is
+ * a single entry map. For Gralloc V1, one physical buffer can be shared between different
+ * streams in one stream set, so this entry may include multiple entries, where the different
+ * graphic buffers have the same common Gralloc backing store.
+ */
+ typedef int StreamId;
+ typedef KeyedVector<StreamId, GraphicBufferEntry> BufferEntry;
+
+ typedef std::list<BufferEntry> BufferList;
+
+ /**
+ * Stream info map (indexed by stream ID) tracks all the streams registered to a particular
+ * stream set.
+ */
+ typedef KeyedVector<StreamId, StreamInfo> InfoMap;
+
+ /**
+ * Stream set buffer count map (indexed by stream ID) tracks all buffer counts of the streams
+ * registered to a particular stream set.
+ */
+ typedef KeyedVector<StreamId, size_t> BufferCountMap;
+
+ /**
+ * StreamSet keeps track of the stream info, free buffer list and hand-out buffer counts for
+ * each stream set.
+ */
+ struct StreamSet {
+ /**
+ * Stream set buffer count water mark representing the max number of allocated buffers
+ * (hand-out buffers + free buffers) count for each stream set. For a given stream set, when
+ * getBufferForStream() is called on this buffer manager, if the total allocated buffer
+ * count exceeds this water mark, the buffer manager will attempt to reduce it as follows:
+ *
+ * In getBufferForStream(), find a buffer associated with other streams (inside the same
+ * stream set) on the free buffer list and free it. For Gralloc V1, can just free the top
+ * of the free buffer list if the physical buffer sharing in this stream is supported.
+ *
+ * For a particular stream set, a larger allocatedBufferWaterMark increases the memory
+ * footprint of the stream set, but reduces the chance that getBufferForStream() will have
+ * to allocate a new buffer. We assume that the streams in one stream set are not streaming
+ * simultaneously, the max allocated buffer count water mark for a stream set will the max
+ * of all streams' total buffer counts. This will avoid new buffer allocation in steady
+ * streaming state.
+ */
+ size_t allocatedBufferWaterMark;
+ /**
+ * The stream info for all streams in this set
+ */
+ InfoMap streamInfoMap;
+ /**
+ * The free buffer list for all the buffers belong to this set. The free buffers are
+ * returned by the returnBufferForStream() call, and available for reuse.
+ */
+ BufferList freeBuffers;
+ /**
+ * The count of the buffers that were handed out to the streams of this set.
+ */
+ BufferCountMap handoutBufferCountMap;
+ StreamSet() {
+ allocatedBufferWaterMark = 0;
+ }
+ };
+
+ /**
+ * Stream set map managed by this buffer manager.
+ */
+ typedef int StreamSetId;
+ KeyedVector<StreamSetId, StreamSet> mStreamSetMap;
+
+ // TODO: There is no easy way to query the Gralloc version in this code yet, we have different
+ // code paths for different Gralloc versions, hardcode something here for now.
+ const uint32_t mGrallocVersion = GRALLOC_DEVICE_API_VERSION_0_1;
+
+ /**
+ * Check if this stream was successfully registered already. This method needs to be called with
+ * mLock held.
+ */
+ bool checkIfStreamRegisteredLocked(int streamId, int streamSetId) const;
+
+ /**
+ * Add a buffer entry to the BufferList. This method needs to be called with mLock held.
+ */
+ status_t addBufferToBufferListLocked(BufferList &bufList, const BufferEntry &buffer);
+
+ /**
+ * Remove all buffers from the BufferList.
+ *
+ * Note that this doesn't mean that the buffers are freed after this call. A buffer is freed
+ * only if all other references to it are dropped.
+ *
+ * This method needs to be called with mLock held.
+ */
+ status_t removeBuffersFromBufferListLocked(BufferList &bufList, int streamId);
+
+ /**
+ * Get the first available buffer from the buffer list for this stream. The graphicBuffer inside
+ * this entry will be NULL if there is no any GraphicBufferEntry found. After this call, the
+ * GraphicBufferEntry will be removed from the BufferList if a GraphicBufferEntry is found.
+ *
+ * This method needs to be called with mLock held.
+ *
+ */
+ GraphicBufferEntry getFirstBufferFromBufferListLocked(BufferList& buffers, int streamId);
+};
+
+} // namespace camera3
+} // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA3_BUFFER_MANAGER_H
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 6c07aef..ea1e5f6 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -167,6 +167,9 @@
return res;
}
+ /** Create buffer manager */
+ mBufferManager = new Camera3BufferManager();
+
bool aeLockAvailable = false;
camera_metadata_ro_entry aeLockAvailableEntry;
res = find_camera_metadata_ro_entry(info.static_camera_characteristics,
@@ -293,6 +296,7 @@
mRequestThread.clear();
mStatusTracker.clear();
+ mBufferManager.clear();
hal3Device = mHal3Device;
}
@@ -502,6 +506,10 @@
mOutputStreams[i]->dump(fd,args);
}
+ lines = String8(" Camera3 Buffer Manager:\n");
+ write(fd, lines.string(), lines.size());
+ mBufferManager->dump(fd, args);
+
lines = String8(" In-flight requests:\n");
if (mInFlightMap.size() == 0) {
lines.append(" None\n");
@@ -926,7 +934,7 @@
status_t Camera3Device::createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format, android_dataspace dataSpace,
- camera3_stream_rotation_t rotation, int *id) {
+ camera3_stream_rotation_t rotation, int *id, int streamSetId) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -963,6 +971,11 @@
assert(mStatus != STATUS_ACTIVE);
sp<Camera3OutputStream> newStream;
+ // Overwrite stream set id to invalid for HAL3.1 or lower, as buffer manager does support
+ // such devices.
+ if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+ streamSetId = CAMERA3_STREAM_SET_ID_INVALID;
+ }
if (format == HAL_PIXEL_FORMAT_BLOB) {
ssize_t blobBufferSize;
if (dataSpace != HAL_DATASPACE_DEPTH) {
@@ -979,7 +992,7 @@
}
}
newStream = new Camera3OutputStream(mNextStreamId, consumer,
- width, height, blobBufferSize, format, dataSpace, rotation);
+ width, height, blobBufferSize, format, dataSpace, rotation, streamSetId);
} else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height);
if (rawOpaqueBufferSize <= 0) {
@@ -987,13 +1000,22 @@
return BAD_VALUE;
}
newStream = new Camera3OutputStream(mNextStreamId, consumer,
- width, height, rawOpaqueBufferSize, format, dataSpace, rotation);
+ width, height, rawOpaqueBufferSize, format, dataSpace, rotation, streamSetId);
} else {
newStream = new Camera3OutputStream(mNextStreamId, consumer,
- width, height, format, dataSpace, rotation);
+ width, height, format, dataSpace, rotation, streamSetId);
}
newStream->setStatusTracker(mStatusTracker);
+ /**
+ * Camera3 Buffer manager is only supported by HAL3.2 onwards, as the older HALs requires
+ * buffers to be statically allocated for internal static buffer registration, while the
+ * buffers provided by buffer manager are really dynamically allocated.
+ */
+ if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
+ newStream->setBufferManager(mBufferManager);
+ }
+
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 7e20b0d..f70a153 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -29,6 +29,7 @@
#include "common/CameraDeviceBase.h"
#include "device3/StatusTracker.h"
+#include "device3/Camera3BufferManager.h"
/**
* Function pointer types with C calling convention to
@@ -97,7 +98,8 @@
// stream, reconfiguring device, and unpausing.
virtual status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID);
virtual status_t createInputStream(
uint32_t width, uint32_t height, int format,
int *id);
@@ -721,6 +723,13 @@
sp<camera3::StatusTracker> mStatusTracker;
/**
+ * Graphic buffer manager for output streams. Each device has a buffer manager, which is used
+ * by the output streams to get and return buffers if these streams are registered to this
+ * buffer manager.
+ */
+ sp<camera3::Camera3BufferManager> mBufferManager;
+
+ /**
* Thread for preparing streams
*/
class PreparerThread : private Thread, public virtual RefBase {
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 1d9d04f..fe04eb1 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -38,7 +38,7 @@
status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *buffer) {
ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", mId);
+ ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
}
@@ -46,7 +46,7 @@
const camera3_stream_buffer &buffer,
nsecs_t timestamp) {
ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", mId);
+ ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
}
@@ -57,7 +57,7 @@
/*out*/
sp<Fence> *releaseFenceOut) {
ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", mId);
+ ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
}
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 7b298e6..4824974 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -31,9 +31,9 @@
Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
Camera3Stream(id, type,
- width, height, maxSize, format, dataSpace, rotation),
+ width, height, maxSize, format, dataSpace, rotation, setId),
mTotalBufferCount(0),
mHandoutTotalBufferCount(0),
mHandoutOutputBufferCount(0),
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index f5727e8..35dda39 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -34,7 +34,8 @@
protected:
Camera3IOStreamBase(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
public:
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 2504bfd..7dab2e3 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -169,7 +169,7 @@
if (producer == NULL) {
return BAD_VALUE;
} else if (mProducer == NULL) {
- ALOGE("%s: No input stream is configured");
+ ALOGE("%s: No input stream is configured", __FUNCTION__);
return INVALID_OPERATION;
}
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index a5aa1fa..c721144 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -34,28 +34,34 @@
Camera3OutputStream::Camera3OutputStream(int id,
sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
- /*maxSize*/0, format, dataSpace, rotation),
+ /*maxSize*/0, format, dataSpace, rotation, setId),
mConsumer(consumer),
mTransform(0),
- mTraceFirstBuffer(true) {
+ mTraceFirstBuffer(true),
+ mUseBufferManager(false) {
if (mConsumer == NULL) {
ALOGE("%s: Consumer is NULL!", __FUNCTION__);
mState = STATE_ERROR;
}
+
+ if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
+ mBufferReleasedListener = new BufferReleasedListener(this);
+ }
}
Camera3OutputStream::Camera3OutputStream(int id,
sp<Surface> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize,
- format, dataSpace, rotation),
+ format, dataSpace, rotation, setId),
mConsumer(consumer),
mTransform(0),
- mTraceFirstBuffer(true) {
+ mTraceFirstBuffer(true),
+ mUseBufferManager(false) {
if (format != HAL_PIXEL_FORMAT_BLOB && format != HAL_PIXEL_FORMAT_RAW_OPAQUE) {
ALOGE("%s: Bad format for size-only stream: %d", __FUNCTION__,
@@ -67,17 +73,28 @@
ALOGE("%s: Consumer is NULL!", __FUNCTION__);
mState = STATE_ERROR;
}
+
+ if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
+ mBufferReleasedListener = new BufferReleasedListener(this);
+ }
}
Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height,
int format,
android_dataspace dataSpace,
- camera3_stream_rotation_t rotation) :
+ camera3_stream_rotation_t rotation,
+ int setId) :
Camera3IOStreamBase(id, type, width, height,
/*maxSize*/0,
- format, dataSpace, rotation),
- mTransform(0) {
+ format, dataSpace, rotation, setId),
+ mTransform(0),
+ mTraceFirstBuffer(true),
+ mUseBufferManager(false) {
+
+ if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
+ mBufferReleasedListener = new BufferReleasedListener(this);
+ }
// Subclasses expected to initialize mConsumer themselves
}
@@ -96,28 +113,46 @@
}
ANativeWindowBuffer* anb;
- int fenceFd;
+ int fenceFd = -1;
+ if (mUseBufferManager) {
+ sp<GraphicBuffer> gb;
+ res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, &fenceFd);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Can't get next output buffer from buffer manager: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
+ // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
+ // successful return.
+ anb = gb.get();
+ res = mConsumer->attachBuffer(anb);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
+ } else {
+ /**
+ * Release the lock briefly to avoid deadlock for below scenario:
+ * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
+ * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
+ * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
+ * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
+ * StreamingProcessor lock.
+ * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
+ * and try to lock bufferQueue lock.
+ * Then there is circular locking dependency.
+ */
+ sp<ANativeWindow> currentConsumer = mConsumer;
+ mLock.unlock();
- /**
- * Release the lock briefly to avoid deadlock for below scenario:
- * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
- * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
- * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
- * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
- * StreamingProcessor lock.
- * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
- * and try to lock bufferQueue lock.
- * Then there is circular locking dependency.
- */
- sp<ANativeWindow> currentConsumer = mConsumer;
- mLock.unlock();
-
- res = currentConsumer->dequeueBuffer(currentConsumer.get(), &anb, &fenceFd);
- mLock.lock();
- if (res != OK) {
- ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- return res;
+ res = currentConsumer->dequeueBuffer(currentConsumer.get(), &anb, &fenceFd);
+ mLock.lock();
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
}
/**
@@ -270,9 +305,9 @@
ALOG_ASSERT(mConsumer != 0, "mConsumer should never be NULL");
- // Configure consumer-side ANativeWindow interface
- res = native_window_api_connect(mConsumer.get(),
- NATIVE_WINDOW_API_CAMERA);
+ // Configure consumer-side ANativeWindow interface. The listener may be used
+ // to notify buffer manager (if it is used) of the returned buffers.
+ res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA, /*listener*/mBufferReleasedListener);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mId);
@@ -366,6 +401,30 @@
__FUNCTION__, mTransform, strerror(-res), res);
}
+ /**
+ * Camera3 Buffer manager is only supported by HAL3.2 onwards, as the older HALs requires
+ * buffers to be statically allocated for internal static buffer registration, while the
+ * buffers provided by buffer manager are really dynamically allocated. Camera3Device only
+ * sets the mBufferManager if device version is >= HAL3.2, which guarantees that the buffer
+ * manager setup is skipped in below code.
+ */
+ if (mBufferManager != 0 && mSetId > CAMERA3_STREAM_SET_ID_INVALID) {
+ StreamInfo streamInfo(
+ getId(), getStreamSetId(), getWidth(), getHeight(), getFormat(), getDataSpace(),
+ camera3_stream::usage, mTotalBufferCount, /*isConfigured*/true);
+ res = mBufferManager->registerStream(streamInfo);
+ if (res == OK) {
+ // Disable buffer allocation for this BufferQueue, buffer manager will take over
+ // the buffer allocation responsibility.
+ mConsumer->getIGraphicBufferProducer()->allowAllocation(false);
+ mUseBufferManager = true;
+ } else {
+ ALOGE("%s: Unable to register stream %d to camera3 buffer manager, "
+ "(error %d %s), fall back to BufferQueue for buffer management!",
+ __FUNCTION__, mId, res, strerror(-res));
+ }
+ }
+
return OK;
}
@@ -376,6 +435,8 @@
return res;
}
+ ALOGV("%s: disconnecting stream %d from native window", __FUNCTION__, getId());
+
res = native_window_api_disconnect(mConsumer.get(),
NATIVE_WINDOW_API_CAMERA);
@@ -396,6 +457,21 @@
return res;
}
+ // Since device is already idle, there is no getBuffer call to buffer manager, unregister the
+ // stream at this point should be safe.
+ if (mUseBufferManager) {
+ res = mBufferManager->unregisterStream(getId(), getStreamSetId());
+ if (res != OK) {
+ ALOGE("%s: Unable to unregister stream %d from buffer manager "
+ "(error %d %s)", __FUNCTION__, mId, res, strerror(-res));
+ mState = STATE_ERROR;
+ return res;
+ }
+ // Note that, to make prepare/teardown case work, we must not mBufferManager.clear(), as
+ // the stream is still in usable state after this call.
+ mUseBufferManager = false;
+ }
+
mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG
: STATE_CONSTRUCTED;
return OK;
@@ -437,6 +513,59 @@
return (usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) != 0;
}
+status_t Camera3OutputStream::setBufferManager(sp<Camera3BufferManager> bufferManager) {
+ Mutex::Autolock l(mLock);
+ if (mState != STATE_CONSTRUCTED) {
+ ALOGE("%s: this method can only be called when stream in in CONSTRUCTED state.",
+ __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ mBufferManager = bufferManager;
+
+ return OK;
+}
+
+void Camera3OutputStream::BufferReleasedListener::onBufferReleased() {
+ sp<Camera3OutputStream> stream = mParent.promote();
+ if (stream == nullptr) {
+ ALOGV("%s: Parent camera3 output stream was destroyed", __FUNCTION__);
+ return;
+ }
+
+ Mutex::Autolock l(stream->mLock);
+ if (!(stream->mUseBufferManager)) {
+ return;
+ }
+
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ int fenceFd = -1;
+ status_t res = stream->mConsumer->detachNextBuffer(&buffer, &fence);
+ if (res == NO_MEMORY) {
+ // This may rarely happen, which indicates that the released buffer was freed by other
+ // call (e.g., attachBuffer, dequeueBuffer etc.) before reaching here. We should notify the
+ // buffer manager that this buffer has been freed. It's not fatal, but should be avoided,
+ // therefore log a warning.
+ buffer = 0;
+ ALOGW("%s: the released buffer has already been freed by the buffer queue!", __FUNCTION__);
+ } else if (res != OK) {
+ // Other errors are fatal.
+ ALOGE("%s: detach next buffer failed: %s (%d).", __FUNCTION__, strerror(-res), res);
+ stream->mState = STATE_ERROR;
+ return;
+ }
+
+ if (fence!= 0 && fence->isValid()) {
+ fenceFd = fence->dup();
+ }
+ res = stream->mBufferManager->returnBufferForStream(stream->getId(), stream->getStreamSetId(),
+ buffer, fenceFd);
+ if (res != OK) {
+ ALOGE("%s: return buffer to buffer manager failed: %s (%d).", __FUNCTION__,
+ strerror(-res), res);
+ stream->mState = STATE_ERROR;
+ }
+}
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 3c083ec..e222e2c 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -18,16 +18,54 @@
#define ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H
#include <utils/RefBase.h>
+#include <gui/IProducerListener.h>
#include <gui/Surface.h>
#include "Camera3Stream.h"
#include "Camera3IOStreamBase.h"
#include "Camera3OutputStreamInterface.h"
+#include "Camera3BufferManager.h"
namespace android {
namespace camera3 {
+class Camera3BufferManager;
+
+/**
+ * Stream info structure that holds the necessary stream info for buffer manager to use for
+ * buffer allocation and management.
+ */
+struct StreamInfo {
+ int streamId;
+ int streamSetId;
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ android_dataspace dataSpace;
+ uint32_t combinedUsage;
+ size_t totalBufferCount;
+ bool isConfigured;
+ StreamInfo(int id = CAMERA3_STREAM_ID_INVALID,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID,
+ uint32_t w = 0,
+ uint32_t h = 0,
+ uint32_t fmt = 0,
+ android_dataspace ds = HAL_DATASPACE_UNKNOWN,
+ uint32_t usage = 0,
+ size_t bufferCount = 0,
+ bool configured = false) :
+ streamId(id),
+ streamSetId(setId),
+ width(w),
+ height(h),
+ format(fmt),
+ dataSpace(ds),
+ combinedUsage(usage),
+ totalBufferCount(bufferCount),
+ isConfigured(configured){}
+};
+
/**
* A class for managing a single stream of output data from the camera device.
*/
@@ -37,18 +75,24 @@
public:
/**
* Set up a stream for formats that have 2 dimensions, such as RAW and YUV.
+ * A valid stream set id needs to be set to support buffer sharing between multiple
+ * streams.
*/
Camera3OutputStream(int id, sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
/**
* Set up a stream for formats that have a variable buffer size for the same
* dimensions, such as compressed JPEG.
+ * A valid stream set id needs to be set to support buffer sharing between multiple
+ * streams.
*/
Camera3OutputStream(int id, sp<Surface> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
virtual ~Camera3OutputStream();
@@ -69,10 +113,32 @@
*/
bool isVideoStream() const;
+ class BufferReleasedListener : public BnProducerListener {
+ public:
+ BufferReleasedListener(wp<Camera3OutputStream> parent) : mParent(parent) {}
+
+ /**
+ * Implementation of IProducerListener, used to notify this stream that the consumer
+ * has returned a buffer and it is ready to return to Camera3BufferManager for reuse.
+ */
+ virtual void onBufferReleased();
+
+ private:
+ wp<Camera3OutputStream> mParent;
+ };
+
+ /**
+ * Set the graphic buffer manager to get/return the stream buffers.
+ *
+ * It is only legal to call this method when stream is in STATE_CONSTRUCTED state.
+ */
+ status_t setBufferManager(sp<Camera3BufferManager> bufferManager);
+
protected:
Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
/**
* Note that we release the lock briefly in this function
@@ -98,6 +164,22 @@
String8 mConsumerName;
/**
+ * GraphicBuffer manager this stream is registered to. Used to replace the buffer
+ * allocation/deallocation role of BufferQueue.
+ */
+ sp<Camera3BufferManager> mBufferManager;
+
+ /**
+ * Buffer released listener, used to notify the buffer manager that a buffer is released
+ * from consumer side.
+ */
+ sp<BufferReleasedListener> mBufferReleasedListener;
+
+ /**
+ * Flag indicating if the buffer manager is used to allocate the stream buffers
+ */
+ bool mUseBufferManager;
+ /**
* Internal Camera3Stream interface
*/
virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 141f6c3..ed3ab97 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -47,13 +47,19 @@
Camera3Stream::Camera3Stream(int id,
camera3_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
camera3_stream(),
mId(id),
+ mSetId(setId),
mName(String8::format("Camera3Stream[%d]", id)),
mMaxSize(maxSize),
mState(STATE_CONSTRUCTED),
mStatusId(StatusTracker::NO_STATUS_ID),
+ oldUsage(0),
+ oldMaxBuffers(0),
+ mStreamUnpreparable(false),
+ mPrepared(false),
+ mPreparedBufferIdx(0),
mLastMaxCount(Camera3StreamInterface::ALLOCATE_PIPELINE_MAX) {
camera3_stream::stream_type = type;
@@ -77,6 +83,10 @@
return mId;
}
+int Camera3Stream::getStreamSetId() const {
+ return mSetId;
+}
+
uint32_t Camera3Stream::getWidth() const {
return camera3_stream::width;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 753280b..fe51ab5 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -130,6 +130,11 @@
int getId() const;
/**
+ * Get the output stream set id.
+ */
+ int getStreamSetId() const;
+
+ /**
* Get the stream's dimensions and format
*/
uint32_t getWidth() const;
@@ -350,6 +355,21 @@
protected:
const int mId;
+ /**
+ * Stream set id, used to indicate which group of this stream belongs to for buffer sharing
+ * across multiple streams.
+ *
+ * The default value is set to CAMERA3_STREAM_SET_ID_INVALID, which indicates that this stream
+ * doesn't intend to share buffers with any other streams, and this stream will fall back to
+ * the existing BufferQueue mechanism to manage the buffer allocations and buffer circulation.
+ * When a valid stream set id is set, this stream intends to use the Camera3BufferManager to
+ * manage the buffer allocations; the BufferQueue will only handle the buffer transaction
+ * between the producer and consumer. For this case, upon successfully registration, the streams
+ * with the same stream set id will potentially share the buffers allocated by
+ * Camera3BufferManager.
+ */
+ const int mSetId;
+
const String8 mName;
// Zero for formats with fixed buffer size for given dimensions.
const size_t mMaxSize;
@@ -367,7 +387,8 @@
Camera3Stream(int id, camera3_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId);
/**
* Interface to be implemented by derived classes
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 54009ae..3f7e7a7 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -26,6 +26,20 @@
namespace camera3 {
+enum {
+ /**
+ * This stream set ID indicates that the set ID is invalid, and this stream doesn't intend to
+ * share buffers with any other stream. It is illegal to register this kind of stream to
+ * Camera3BufferManager.
+ */
+ CAMERA3_STREAM_SET_ID_INVALID = -1,
+
+ /**
+ * Invalid output stream ID.
+ */
+ CAMERA3_STREAM_ID_INVALID = -1,
+};
+
class StatusTracker;
/**
@@ -45,6 +59,11 @@
virtual int getId() const = 0;
/**
+ * Get the output stream set id.
+ */
+ virtual int getStreamSetId() const = 0;
+
+ /**
* Get the stream's dimensions and format
*/
virtual uint32_t getWidth() const = 0;
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h
index 243ea31..28dc5d5 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.h
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h
@@ -189,4 +189,4 @@
} // namespace android
-#endif // ANDROID_GUI_CPUCONSUMER_H
+#endif // ANDROID_GUI_RINGBUFFERCONSUMER_H