Camera3StreamSplitter: Split out a refactorable file
Doing this so that I can easily flag guard the splitter and A/B test the
different implementations.
BYPASS_IGBP_IGBC_API_REASON=warren buffers
Bug: 340933206
Flag: com.android.graphics.libgui.flags.wb_stream_splitter
Test: atest cameraservice_test
Change-Id: Ie6769b1b2fba6d63fb054c815d19e2d78ea853e6
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index 38476a4..a74b6d6 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -58,6 +58,7 @@
"libcamera_metadata",
"libfmq",
"libgui",
+ "libguiflags",
"libhardware",
"libhidlbase",
"libimage_io",
@@ -169,6 +170,7 @@
"device3/Camera3OutputStreamInterface.cpp",
"device3/Camera3OutputUtils.cpp",
"device3/Camera3DeviceInjectionMethods.cpp",
+ "device3/deprecated/DeprecatedCamera3StreamSplitter.cpp",
"device3/UHRCropAndMeteringRegionMapper.cpp",
"device3/PreviewFrameSpacer.cpp",
"device3/hidl/HidlCamera3Device.cpp",
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 485f3f0..187bd93 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -18,6 +18,8 @@
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
+#include "Flags.h"
+
#include "Camera3SharedOutputStream.h"
namespace android {
@@ -59,7 +61,11 @@
status_t Camera3SharedOutputStream::connectStreamSplitterLocked() {
status_t res = OK;
- mStreamSplitter = new Camera3StreamSplitter(mUseHalBufManager);
+#if USE_NEW_STREAM_SPLITTER
+ mStreamSplitter = sp<Camera3StreamSplitter>::make(mUseHalBufManager);
+#else
+ mStreamSplitter = sp<DeprecatedCamera3StreamSplitter>::make(mUseHalBufManager);
+#endif // USE_NEW_STREAM_SPLITTER
uint64_t usage = 0;
getEndpointUsage(&usage);
@@ -90,7 +96,11 @@
// Attach the buffer to the splitter output queues. This could block if
// the output queue doesn't have any empty slot. So unlock during the course
// of attachBufferToOutputs.
+#if USE_NEW_STREAM_SPLITTER
sp<Camera3StreamSplitter> splitter = mStreamSplitter;
+#else
+ sp<DeprecatedCamera3StreamSplitter> splitter = mStreamSplitter;
+#endif // USE_NEW_STREAM_SPLITTER
mLock.unlock();
res = splitter->attachBufferToOutputs(anb, surface_ids);
mLock.lock();
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index 818ce17..ae11507 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -18,9 +18,17 @@
#define ANDROID_SERVERS_CAMERA3_SHARED_OUTPUT_STREAM_H
#include <array>
-#include "Camera3StreamSplitter.h"
+
+#include "Flags.h"
+
#include "Camera3OutputStream.h"
+#if USE_NEW_STREAM_SPLITTER
+#include "Camera3StreamSplitter.h"
+#else
+#include "deprecated/DeprecatedCamera3StreamSplitter.h"
+#endif // USE_NEW_STREAM_SPLITTER
+
namespace android {
namespace camera3 {
@@ -106,8 +114,11 @@
* The Camera3StreamSplitter object this stream uses for stream
* sharing.
*/
+#if USE_NEW_STREAM_SPLITTER
sp<Camera3StreamSplitter> mStreamSplitter;
-
+#else
+ sp<DeprecatedCamera3StreamSplitter> mStreamSplitter;
+#endif // USE_NEW_STREAM_SPLITTER
/**
* Initialize stream splitter.
*/
diff --git a/services/camera/libcameraservice/device3/Flags.h b/services/camera/libcameraservice/device3/Flags.h
new file mode 100644
index 0000000..ca0006b
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Flags.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#pragma once
+
+#include <com_android_graphics_libgui_flags.h>
+
+#ifndef USE_NEW_STREAM_SPLITTER
+
+#define USE_NEW_STREAM_SPLITTER \
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_STREAM_SPLITTER) && \
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+#endif // USE_NEW_STREAM_SPLITTER
diff --git a/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.cpp
new file mode 100644
index 0000000..c1113e5
--- /dev/null
+++ b/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.cpp
@@ -0,0 +1,820 @@
+/*
+ * Copyright 2014,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.
+ */
+
+#include <inttypes.h>
+
+#define LOG_TAG "DeprecatedCamera3StreamSplitter"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+// #define LOG_NDEBUG 0
+
+#include <camera/StringUtils.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <binder/ProcessState.h>
+
+#include <utils/Trace.h>
+
+#include <cutils/atomic.h>
+
+#include "../Camera3Stream.h"
+
+#include "DeprecatedCamera3StreamSplitter.h"
+
+namespace android {
+
+status_t DeprecatedCamera3StreamSplitter::connect(
+ const std::unordered_map<size_t, sp<Surface>>& surfaces, uint64_t consumerUsage,
+ uint64_t producerUsage, size_t halMaxBuffers, uint32_t width, uint32_t height,
+ android::PixelFormat format, sp<Surface>* consumer, int64_t dynamicRangeProfile) {
+ ATRACE_CALL();
+ if (consumer == nullptr) {
+ SP_LOGE("%s: consumer pointer is NULL", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+ status_t res = OK;
+
+ if (mOutputs.size() > 0 || mConsumer != nullptr) {
+ SP_LOGE("%s: already connected", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (mBuffers.size() > 0) {
+ SP_LOGE("%s: still has %zu pending buffers", __FUNCTION__, mBuffers.size());
+ return BAD_VALUE;
+ }
+
+ mMaxHalBuffers = halMaxBuffers;
+ mConsumerName = getUniqueConsumerName();
+ mDynamicRangeProfile = dynamicRangeProfile;
+ // Add output surfaces. This has to be before creating internal buffer queue
+ // in order to get max consumer side buffers.
+ for (auto& it : surfaces) {
+ if (it.second == nullptr) {
+ SP_LOGE("%s: Fatal: surface is NULL", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ res = addOutputLocked(it.first, it.second);
+ if (res != OK) {
+ SP_LOGE("%s: Failed to add output surface: %s(%d)", __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ }
+
+ // Create BufferQueue for input
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+
+ // Allocate 1 extra buffer to handle the case where all buffers are detached
+ // from input, and attached to the outputs. In this case, the input queue's
+ // dequeueBuffer can still allocate 1 extra buffer before being blocked by
+ // the output's attachBuffer().
+ mMaxConsumerBuffers++;
+ mBufferItemConsumer = new BufferItemConsumer(mConsumer, consumerUsage, mMaxConsumerBuffers);
+ if (mBufferItemConsumer == nullptr) {
+ return NO_MEMORY;
+ }
+ mConsumer->setConsumerName(toString8(mConsumerName));
+
+ *consumer = new Surface(mProducer);
+ if (*consumer == nullptr) {
+ return NO_MEMORY;
+ }
+
+ res = mProducer->setAsyncMode(true);
+ if (res != OK) {
+ SP_LOGE("%s: Failed to enable input queue async mode: %s(%d)", __FUNCTION__, strerror(-res),
+ res);
+ return res;
+ }
+
+ res = mConsumer->consumerConnect(this, /* controlledByApp */ false);
+
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mProducerUsage = producerUsage;
+ mAcquiredInputBuffers = 0;
+
+ SP_LOGV("%s: connected", __FUNCTION__);
+ return res;
+}
+
+status_t DeprecatedCamera3StreamSplitter::getOnFrameAvailableResult() {
+ ATRACE_CALL();
+ return mOnFrameAvailableRes.load();
+}
+
+void DeprecatedCamera3StreamSplitter::disconnect() {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ for (auto& notifier : mNotifiers) {
+ sp<IGraphicBufferProducer> producer = notifier.first;
+ sp<OutputListener> listener = notifier.second;
+ IInterface::asBinder(producer)->unlinkToDeath(listener);
+ }
+ mNotifiers.clear();
+
+ for (auto& output : mOutputs) {
+ if (output.second != nullptr) {
+ output.second->disconnect(NATIVE_WINDOW_API_CAMERA);
+ }
+ }
+ mOutputs.clear();
+ mOutputSurfaces.clear();
+ mOutputSlots.clear();
+ mConsumerBufferCount.clear();
+
+ if (mConsumer.get() != nullptr) {
+ mConsumer->consumerDisconnect();
+ }
+
+ if (mBuffers.size() > 0) {
+ SP_LOGW("%zu buffers still being tracked", mBuffers.size());
+ mBuffers.clear();
+ }
+
+ mMaxHalBuffers = 0;
+ mMaxConsumerBuffers = 0;
+ mAcquiredInputBuffers = 0;
+ SP_LOGV("%s: Disconnected", __FUNCTION__);
+}
+
+DeprecatedCamera3StreamSplitter::DeprecatedCamera3StreamSplitter(bool useHalBufManager)
+ : mUseHalBufManager(useHalBufManager) {}
+
+DeprecatedCamera3StreamSplitter::~DeprecatedCamera3StreamSplitter() {
+ disconnect();
+}
+
+status_t DeprecatedCamera3StreamSplitter::addOutput(size_t surfaceId,
+ const sp<Surface>& outputQueue) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+ status_t res = addOutputLocked(surfaceId, outputQueue);
+
+ if (res != OK) {
+ SP_LOGE("%s: addOutputLocked failed %d", __FUNCTION__, res);
+ return res;
+ }
+
+ if (mMaxConsumerBuffers > mAcquiredInputBuffers) {
+ res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers);
+ }
+
+ return res;
+}
+
+void DeprecatedCamera3StreamSplitter::setHalBufferManager(bool enabled) {
+ Mutex::Autolock lock(mMutex);
+ mUseHalBufManager = enabled;
+}
+
+status_t DeprecatedCamera3StreamSplitter::addOutputLocked(size_t surfaceId,
+ const sp<Surface>& outputQueue) {
+ ATRACE_CALL();
+ if (outputQueue == nullptr) {
+ SP_LOGE("addOutput: outputQueue must not be NULL");
+ return BAD_VALUE;
+ }
+
+ if (mOutputs[surfaceId] != nullptr) {
+ SP_LOGE("%s: surfaceId: %u already taken!", __FUNCTION__, (unsigned)surfaceId);
+ return BAD_VALUE;
+ }
+
+ status_t res = native_window_set_buffers_dimensions(outputQueue.get(), mWidth, mHeight);
+ if (res != NO_ERROR) {
+ SP_LOGE("addOutput: failed to set buffer dimensions (%d)", res);
+ return res;
+ }
+ res = native_window_set_buffers_format(outputQueue.get(), mFormat);
+ if (res != OK) {
+ ALOGE("%s: Unable to configure stream buffer format %#x for surfaceId %zu", __FUNCTION__,
+ mFormat, surfaceId);
+ return res;
+ }
+
+ sp<IGraphicBufferProducer> gbp = outputQueue->getIGraphicBufferProducer();
+ // Connect to the buffer producer
+ sp<OutputListener> listener(new OutputListener(this, gbp));
+ IInterface::asBinder(gbp)->linkToDeath(listener);
+ res = outputQueue->connect(NATIVE_WINDOW_API_CAMERA, listener);
+ if (res != NO_ERROR) {
+ SP_LOGE("addOutput: failed to connect (%d)", res);
+ return res;
+ }
+
+ // Query consumer side buffer count, and update overall buffer count
+ int maxConsumerBuffers = 0;
+ res = static_cast<ANativeWindow*>(outputQueue.get())
+ ->query(outputQueue.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &maxConsumerBuffers);
+ if (res != OK) {
+ SP_LOGE("%s: Unable to query consumer undequeued buffer count"
+ " for surface",
+ __FUNCTION__);
+ return res;
+ }
+
+ SP_LOGV("%s: Consumer wants %d buffers, Producer wants %zu", __FUNCTION__, maxConsumerBuffers,
+ mMaxHalBuffers);
+ // The output slot count requirement can change depending on the current amount
+ // of outputs and incoming buffer consumption rate. To avoid any issues with
+ // insufficient slots, set their count to the maximum supported. The output
+ // surface buffer allocation is disabled so no real buffers will get allocated.
+ size_t totalBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
+ res = native_window_set_buffer_count(outputQueue.get(), totalBufferCount);
+ if (res != OK) {
+ SP_LOGE("%s: Unable to set buffer count for surface %p", __FUNCTION__, outputQueue.get());
+ return res;
+ }
+
+ // Set dequeueBuffer/attachBuffer timeout if the consumer is not hw composer or hw texture.
+ // We need skip these cases as timeout will disable the non-blocking (async) mode.
+ uint64_t usage = 0;
+ res = native_window_get_consumer_usage(static_cast<ANativeWindow*>(outputQueue.get()), &usage);
+ if (!(usage & (GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE))) {
+ nsecs_t timeout =
+ mUseHalBufManager ? kHalBufMgrDequeueBufferTimeout : kNormalDequeueBufferTimeout;
+ outputQueue->setDequeueTimeout(timeout);
+ }
+
+ res = gbp->allowAllocation(false);
+ if (res != OK) {
+ SP_LOGE("%s: Failed to turn off allocation for outputQueue", __FUNCTION__);
+ return res;
+ }
+
+ // Add new entry into mOutputs
+ mOutputs[surfaceId] = gbp;
+ mOutputSurfaces[surfaceId] = outputQueue;
+ mConsumerBufferCount[surfaceId] = maxConsumerBuffers;
+ if (mConsumerBufferCount[surfaceId] > mMaxHalBuffers) {
+ SP_LOGW("%s: Consumer buffer count %zu larger than max. Hal buffers: %zu", __FUNCTION__,
+ mConsumerBufferCount[surfaceId], mMaxHalBuffers);
+ }
+ mNotifiers[gbp] = listener;
+ mOutputSlots[gbp] = std::make_unique<OutputSlots>(totalBufferCount);
+
+ mMaxConsumerBuffers += maxConsumerBuffers;
+ return NO_ERROR;
+}
+
+status_t DeprecatedCamera3StreamSplitter::removeOutput(size_t surfaceId) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ status_t res = removeOutputLocked(surfaceId);
+ if (res != OK) {
+ SP_LOGE("%s: removeOutputLocked failed %d", __FUNCTION__, res);
+ return res;
+ }
+
+ if (mAcquiredInputBuffers < mMaxConsumerBuffers) {
+ res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers);
+ if (res != OK) {
+ SP_LOGE("%s: setMaxAcquiredBufferCount failed %d", __FUNCTION__, res);
+ return res;
+ }
+ }
+
+ return res;
+}
+
+status_t DeprecatedCamera3StreamSplitter::removeOutputLocked(size_t surfaceId) {
+ if (mOutputs[surfaceId] == nullptr) {
+ SP_LOGE("%s: output surface is not present!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ sp<IGraphicBufferProducer> gbp = mOutputs[surfaceId];
+ // Search and decrement the ref. count of any buffers that are
+ // still attached to the removed surface.
+ std::vector<uint64_t> pendingBufferIds;
+ auto& outputSlots = *mOutputSlots[gbp];
+ for (size_t i = 0; i < outputSlots.size(); i++) {
+ if (outputSlots[i] != nullptr) {
+ pendingBufferIds.push_back(outputSlots[i]->getId());
+ auto rc = gbp->detachBuffer(i);
+ if (rc != NO_ERROR) {
+ // Buffers that fail to detach here will be scheduled for detach in the
+ // input buffer queue and the rest of the registered outputs instead.
+ // This will help ensure that camera stops accessing buffers that still
+ // can get referenced by the disconnected output.
+ mDetachedBuffers.emplace(outputSlots[i]->getId());
+ }
+ }
+ }
+ mOutputs[surfaceId] = nullptr;
+ mOutputSurfaces[surfaceId] = nullptr;
+ mOutputSlots[gbp] = nullptr;
+ for (const auto& id : pendingBufferIds) {
+ decrementBufRefCountLocked(id, surfaceId);
+ }
+
+ auto res = IInterface::asBinder(gbp)->unlinkToDeath(mNotifiers[gbp]);
+ if (res != OK) {
+ SP_LOGE("%s: Failed to unlink producer death listener: %d ", __FUNCTION__, res);
+ return res;
+ }
+
+ res = gbp->disconnect(NATIVE_WINDOW_API_CAMERA);
+ if (res != OK) {
+ SP_LOGE("%s: Unable disconnect from producer interface: %d ", __FUNCTION__, res);
+ return res;
+ }
+
+ mNotifiers[gbp] = nullptr;
+ mMaxConsumerBuffers -= mConsumerBufferCount[surfaceId];
+ mConsumerBufferCount[surfaceId] = 0;
+
+ return res;
+}
+
+status_t DeprecatedCamera3StreamSplitter::outputBufferLocked(
+ const sp<IGraphicBufferProducer>& output, const BufferItem& bufferItem, size_t surfaceId) {
+ ATRACE_CALL();
+ status_t res;
+ IGraphicBufferProducer::QueueBufferInput queueInput(
+ bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp, bufferItem.mDataSpace,
+ bufferItem.mCrop, static_cast<int32_t>(bufferItem.mScalingMode), bufferItem.mTransform,
+ bufferItem.mFence);
+
+ IGraphicBufferProducer::QueueBufferOutput queueOutput;
+
+ uint64_t bufferId = bufferItem.mGraphicBuffer->getId();
+ const BufferTracker& tracker = *(mBuffers[bufferId]);
+ int slot = getSlotForOutputLocked(output, tracker.getBuffer());
+
+ if (mOutputSurfaces[surfaceId] != nullptr) {
+ sp<ANativeWindow> anw = mOutputSurfaces[surfaceId];
+ camera3::Camera3Stream::queueHDRMetadata(
+ bufferItem.mGraphicBuffer->getNativeBuffer()->handle, anw, mDynamicRangeProfile);
+ } else {
+ SP_LOGE("%s: Invalid surface id: %zu!", __FUNCTION__, surfaceId);
+ }
+
+ // In case the output BufferQueue has its own lock, if we hold splitter lock while calling
+ // queueBuffer (which will try to acquire the output lock), the output could be holding its
+ // own lock calling releaseBuffer (which will try to acquire the splitter lock), running into
+ // circular lock situation.
+ mMutex.unlock();
+ res = output->queueBuffer(slot, queueInput, &queueOutput);
+ mMutex.lock();
+
+ SP_LOGV("%s: Queuing buffer to buffer queue %p slot %d returns %d", __FUNCTION__, output.get(),
+ slot, res);
+ // During buffer queue 'mMutex' is not held which makes the removal of
+ //"output" possible. Check whether this is the case and return.
+ if (mOutputSlots[output] == nullptr) {
+ return res;
+ }
+ if (res != OK) {
+ if (res != NO_INIT && res != DEAD_OBJECT) {
+ SP_LOGE("Queuing buffer to output failed (%d)", res);
+ }
+ // If we just discovered that this output has been abandoned, note
+ // that, increment the release count so that we still release this
+ // buffer eventually, and move on to the next output
+ onAbandonedLocked();
+ decrementBufRefCountLocked(bufferItem.mGraphicBuffer->getId(), surfaceId);
+ return res;
+ }
+
+ // If the queued buffer replaces a pending buffer in the async
+ // queue, no onBufferReleased is called by the buffer queue.
+ // Proactively trigger the callback to avoid buffer loss.
+ if (queueOutput.bufferReplaced) {
+ onBufferReplacedLocked(output, surfaceId);
+ }
+
+ return res;
+}
+
+std::string DeprecatedCamera3StreamSplitter::getUniqueConsumerName() {
+ static volatile int32_t counter = 0;
+ return fmt::sprintf("DeprecatedCamera3StreamSplitter-%d", android_atomic_inc(&counter));
+}
+
+status_t DeprecatedCamera3StreamSplitter::notifyBufferReleased(const sp<GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+
+ Mutex::Autolock lock(mMutex);
+
+ uint64_t bufferId = buffer->getId();
+ std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[bufferId]);
+ mBuffers.erase(bufferId);
+
+ return OK;
+}
+
+status_t DeprecatedCamera3StreamSplitter::attachBufferToOutputs(
+ ANativeWindowBuffer* anb, const std::vector<size_t>& surface_ids) {
+ ATRACE_CALL();
+ status_t res = OK;
+
+ Mutex::Autolock lock(mMutex);
+
+ sp<GraphicBuffer> gb(static_cast<GraphicBuffer*>(anb));
+ uint64_t bufferId = gb->getId();
+
+ // Initialize buffer tracker for this input buffer
+ auto tracker = std::make_unique<BufferTracker>(gb, surface_ids);
+
+ for (auto& surface_id : surface_ids) {
+ sp<IGraphicBufferProducer>& gbp = mOutputs[surface_id];
+ if (gbp.get() == nullptr) {
+ // Output surface got likely removed by client.
+ continue;
+ }
+ int slot = getSlotForOutputLocked(gbp, gb);
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ // Buffer is already attached to this output surface.
+ continue;
+ }
+ // Temporarly Unlock the mutex when trying to attachBuffer to the output
+ // queue, because attachBuffer could block in case of a slow consumer. If
+ // we block while holding the lock, onFrameAvailable and onBufferReleased
+ // will block as well because they need to acquire the same lock.
+ mMutex.unlock();
+ res = gbp->attachBuffer(&slot, gb);
+ mMutex.lock();
+ if (res != OK) {
+ SP_LOGE("%s: Cannot attachBuffer from GraphicBufferProducer %p: %s (%d)", __FUNCTION__,
+ gbp.get(), strerror(-res), res);
+ // TODO: might need to detach/cleanup the already attached buffers before return?
+ return res;
+ }
+ if ((slot < 0) || (slot > BufferQueue::NUM_BUFFER_SLOTS)) {
+ SP_LOGE("%s: Slot received %d either bigger than expected maximum %d or negative!",
+ __FUNCTION__, slot, BufferQueue::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ }
+ // During buffer attach 'mMutex' is not held which makes the removal of
+ //"gbp" possible. Check whether this is the case and continue.
+ if (mOutputSlots[gbp] == nullptr) {
+ continue;
+ }
+ auto& outputSlots = *mOutputSlots[gbp];
+ if (static_cast<size_t>(slot + 1) > outputSlots.size()) {
+ outputSlots.resize(slot + 1);
+ }
+ if (outputSlots[slot] != nullptr) {
+ // If the buffer is attached to a slot which already contains a buffer,
+ // the previous buffer will be removed from the output queue. Decrement
+ // the reference count accordingly.
+ decrementBufRefCountLocked(outputSlots[slot]->getId(), surface_id);
+ }
+ SP_LOGV("%s: Attached buffer %p to slot %d on output %p.", __FUNCTION__, gb.get(), slot,
+ gbp.get());
+ outputSlots[slot] = gb;
+ }
+
+ mBuffers[bufferId] = std::move(tracker);
+
+ return res;
+}
+
+void DeprecatedCamera3StreamSplitter::onFrameAvailable(const BufferItem& /*item*/) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ // Acquire and detach the buffer from the input
+ BufferItem bufferItem;
+ status_t res = mConsumer->acquireBuffer(&bufferItem, /* presentWhen */ 0);
+ if (res != NO_ERROR) {
+ SP_LOGE("%s: Acquiring buffer from input failed (%d)", __FUNCTION__, res);
+ mOnFrameAvailableRes.store(res);
+ return;
+ }
+
+ uint64_t bufferId;
+ if (bufferItem.mGraphicBuffer != nullptr) {
+ mInputSlots[bufferItem.mSlot] = bufferItem;
+ } else if (bufferItem.mAcquireCalled) {
+ bufferItem.mGraphicBuffer = mInputSlots[bufferItem.mSlot].mGraphicBuffer;
+ mInputSlots[bufferItem.mSlot].mFrameNumber = bufferItem.mFrameNumber;
+ } else {
+ SP_LOGE("%s: Invalid input graphic buffer!", __FUNCTION__);
+ mOnFrameAvailableRes.store(BAD_VALUE);
+ return;
+ }
+ bufferId = bufferItem.mGraphicBuffer->getId();
+
+ if (mBuffers.find(bufferId) == mBuffers.end()) {
+ SP_LOGE("%s: Acquired buffer doesn't exist in attached buffer map", __FUNCTION__);
+ mOnFrameAvailableRes.store(INVALID_OPERATION);
+ return;
+ }
+
+ mAcquiredInputBuffers++;
+ SP_LOGV("acquired buffer %" PRId64 " from input at slot %d", bufferItem.mGraphicBuffer->getId(),
+ bufferItem.mSlot);
+
+ if (bufferItem.mTransformToDisplayInverse) {
+ bufferItem.mTransform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ }
+
+ // Attach and queue the buffer to each of the outputs
+ BufferTracker& tracker = *(mBuffers[bufferId]);
+
+ SP_LOGV("%s: BufferTracker for buffer %" PRId64 ", number of requests %zu", __FUNCTION__,
+ bufferItem.mGraphicBuffer->getId(), tracker.requestedSurfaces().size());
+ for (const auto id : tracker.requestedSurfaces()) {
+ if (mOutputs[id] == nullptr) {
+ // Output surface got likely removed by client.
+ continue;
+ }
+
+ res = outputBufferLocked(mOutputs[id], bufferItem, id);
+ if (res != OK) {
+ SP_LOGE("%s: outputBufferLocked failed %d", __FUNCTION__, res);
+ mOnFrameAvailableRes.store(res);
+ // If we fail to send buffer to certain output, keep sending to
+ // other outputs.
+ continue;
+ }
+ }
+
+ mOnFrameAvailableRes.store(res);
+}
+
+void DeprecatedCamera3StreamSplitter::onFrameReplaced(const BufferItem& item) {
+ ATRACE_CALL();
+ onFrameAvailable(item);
+}
+
+void DeprecatedCamera3StreamSplitter::decrementBufRefCountLocked(uint64_t id, size_t surfaceId) {
+ ATRACE_CALL();
+
+ if (mBuffers[id] == nullptr) {
+ return;
+ }
+
+ size_t referenceCount = mBuffers[id]->decrementReferenceCountLocked(surfaceId);
+ if (referenceCount > 0) {
+ return;
+ }
+
+ // We no longer need to track the buffer now that it is being returned to the
+ // input. Note that this should happen before we unlock the mutex and call
+ // releaseBuffer, to avoid the case where the same bufferId is acquired in
+ // attachBufferToOutputs resulting in a new BufferTracker with same bufferId
+ // overwrites the current one.
+ std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[id]);
+ mBuffers.erase(id);
+
+ uint64_t bufferId = tracker_ptr->getBuffer()->getId();
+ int consumerSlot = -1;
+ uint64_t frameNumber;
+ auto inputSlot = mInputSlots.begin();
+ for (; inputSlot != mInputSlots.end(); inputSlot++) {
+ if (inputSlot->second.mGraphicBuffer->getId() == bufferId) {
+ consumerSlot = inputSlot->second.mSlot;
+ frameNumber = inputSlot->second.mFrameNumber;
+ break;
+ }
+ }
+ if (consumerSlot == -1) {
+ SP_LOGE("%s: Buffer missing inside input slots!", __FUNCTION__);
+ return;
+ }
+
+ auto detachBuffer = mDetachedBuffers.find(bufferId);
+ bool detach = (detachBuffer != mDetachedBuffers.end());
+ if (detach) {
+ mDetachedBuffers.erase(detachBuffer);
+ mInputSlots.erase(inputSlot);
+ }
+ // Temporarily unlock mutex to avoid circular lock:
+ // 1. This function holds splitter lock, calls releaseBuffer which triggers
+ // onBufferReleased in Camera3OutputStream. onBufferReleased waits on the
+ // OutputStream lock
+ // 2. Camera3SharedOutputStream::getBufferLocked calls
+ // attachBufferToOutputs, which holds the stream lock, and waits for the
+ // splitter lock.
+ sp<IGraphicBufferConsumer> consumer(mConsumer);
+ mMutex.unlock();
+ int res = NO_ERROR;
+ if (consumer != nullptr) {
+ if (detach) {
+ res = consumer->detachBuffer(consumerSlot);
+ } else {
+ res = consumer->releaseBuffer(consumerSlot, frameNumber, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR, tracker_ptr->getMergedFence());
+ }
+ } else {
+ SP_LOGE("%s: consumer has become null!", __FUNCTION__);
+ }
+ mMutex.lock();
+
+ if (res != NO_ERROR) {
+ if (detach) {
+ SP_LOGE("%s: detachBuffer returns %d", __FUNCTION__, res);
+ } else {
+ SP_LOGE("%s: releaseBuffer returns %d", __FUNCTION__, res);
+ }
+ } else {
+ if (mAcquiredInputBuffers == 0) {
+ ALOGW("%s: Acquired input buffer count already at zero!", __FUNCTION__);
+ } else {
+ mAcquiredInputBuffers--;
+ }
+ }
+}
+
+void DeprecatedCamera3StreamSplitter::onBufferReleasedByOutput(
+ const sp<IGraphicBufferProducer>& from) {
+ ATRACE_CALL();
+ sp<Fence> fence;
+
+ int slot = BufferItem::INVALID_BUFFER_SLOT;
+ auto res = from->dequeueBuffer(&slot, &fence, mWidth, mHeight, mFormat, mProducerUsage, nullptr,
+ nullptr);
+ Mutex::Autolock lock(mMutex);
+ handleOutputDequeueStatusLocked(res, slot);
+ if (res != OK) {
+ return;
+ }
+
+ size_t surfaceId = 0;
+ bool found = false;
+ for (const auto& it : mOutputs) {
+ if (it.second == from) {
+ found = true;
+ surfaceId = it.first;
+ break;
+ }
+ }
+ if (!found) {
+ SP_LOGV("%s: output surface not registered anymore!", __FUNCTION__);
+ return;
+ }
+
+ returnOutputBufferLocked(fence, from, surfaceId, slot);
+}
+
+void DeprecatedCamera3StreamSplitter::onBufferReplacedLocked(const sp<IGraphicBufferProducer>& from,
+ size_t surfaceId) {
+ ATRACE_CALL();
+ sp<Fence> fence;
+
+ int slot = BufferItem::INVALID_BUFFER_SLOT;
+ auto res = from->dequeueBuffer(&slot, &fence, mWidth, mHeight, mFormat, mProducerUsage, nullptr,
+ nullptr);
+ handleOutputDequeueStatusLocked(res, slot);
+ if (res != OK) {
+ return;
+ }
+
+ returnOutputBufferLocked(fence, from, surfaceId, slot);
+}
+
+void DeprecatedCamera3StreamSplitter::returnOutputBufferLocked(
+ const sp<Fence>& fence, const sp<IGraphicBufferProducer>& from, size_t surfaceId,
+ int slot) {
+ sp<GraphicBuffer> buffer;
+
+ if (mOutputSlots[from] == nullptr) {
+ // Output surface got likely removed by client.
+ return;
+ }
+
+ auto outputSlots = *mOutputSlots[from];
+ buffer = outputSlots[slot];
+ BufferTracker& tracker = *(mBuffers[buffer->getId()]);
+ // Merge the release fence of the incoming buffer so that the fence we send
+ // back to the input includes all of the outputs' fences
+ if (fence != nullptr && fence->isValid()) {
+ tracker.mergeFence(fence);
+ }
+
+ auto detachBuffer = mDetachedBuffers.find(buffer->getId());
+ bool detach = (detachBuffer != mDetachedBuffers.end());
+ if (detach) {
+ auto res = from->detachBuffer(slot);
+ if (res == NO_ERROR) {
+ outputSlots[slot] = nullptr;
+ } else {
+ SP_LOGE("%s: detach buffer from output failed (%d)", __FUNCTION__, res);
+ }
+ }
+
+ // Check to see if this is the last outstanding reference to this buffer
+ decrementBufRefCountLocked(buffer->getId(), surfaceId);
+}
+
+void DeprecatedCamera3StreamSplitter::handleOutputDequeueStatusLocked(status_t res, int slot) {
+ if (res == NO_INIT) {
+ // If we just discovered that this output has been abandoned, note that,
+ // but we can't do anything else, since buffer is invalid
+ onAbandonedLocked();
+ } else if (res == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ SP_LOGE("%s: Producer needs to re-allocate buffer!", __FUNCTION__);
+ SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
+ } else if (res == IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
+ SP_LOGE("%s: All slot->buffer mapping should be released!", __FUNCTION__);
+ SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
+ } else if (res == NO_MEMORY) {
+ SP_LOGE("%s: No free buffers", __FUNCTION__);
+ } else if (res == WOULD_BLOCK) {
+ SP_LOGE("%s: Dequeue call will block", __FUNCTION__);
+ } else if (res != OK || (slot == BufferItem::INVALID_BUFFER_SLOT)) {
+ SP_LOGE("%s: dequeue buffer from output failed (%d)", __FUNCTION__, res);
+ }
+}
+
+void DeprecatedCamera3StreamSplitter::onAbandonedLocked() {
+ // If this is called from binderDied callback, it means the app process
+ // holding the binder has died. CameraService will be notified of the binder
+ // death, and camera device will be closed, which in turn calls
+ // disconnect().
+ //
+ // If this is called from onBufferReleasedByOutput or onFrameAvailable, one
+ // consumer being abanoned shouldn't impact the other consumer. So we won't
+ // stop the buffer flow.
+ //
+ // In both cases, we don't need to do anything here.
+ SP_LOGV("One of my outputs has abandoned me");
+}
+
+int DeprecatedCamera3StreamSplitter::getSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+ const sp<GraphicBuffer>& gb) {
+ auto& outputSlots = *mOutputSlots[gbp];
+
+ for (size_t i = 0; i < outputSlots.size(); i++) {
+ if (outputSlots[i] == gb) {
+ return (int)i;
+ }
+ }
+
+ SP_LOGV("%s: Cannot find slot for gb %p on output %p", __FUNCTION__, gb.get(), gbp.get());
+ return BufferItem::INVALID_BUFFER_SLOT;
+}
+
+DeprecatedCamera3StreamSplitter::OutputListener::OutputListener(
+ wp<DeprecatedCamera3StreamSplitter> splitter, wp<IGraphicBufferProducer> output)
+ : mSplitter(splitter), mOutput(output) {}
+
+void DeprecatedCamera3StreamSplitter::OutputListener::onBufferReleased() {
+ ATRACE_CALL();
+ sp<DeprecatedCamera3StreamSplitter> splitter = mSplitter.promote();
+ sp<IGraphicBufferProducer> output = mOutput.promote();
+ if (splitter != nullptr && output != nullptr) {
+ splitter->onBufferReleasedByOutput(output);
+ }
+}
+
+void DeprecatedCamera3StreamSplitter::OutputListener::binderDied(const wp<IBinder>& /* who */) {
+ sp<DeprecatedCamera3StreamSplitter> splitter = mSplitter.promote();
+ if (splitter != nullptr) {
+ Mutex::Autolock lock(splitter->mMutex);
+ splitter->onAbandonedLocked();
+ }
+}
+
+DeprecatedCamera3StreamSplitter::BufferTracker::BufferTracker(
+ const sp<GraphicBuffer>& buffer, const std::vector<size_t>& requestedSurfaces)
+ : mBuffer(buffer),
+ mMergedFence(Fence::NO_FENCE),
+ mRequestedSurfaces(requestedSurfaces),
+ mReferenceCount(requestedSurfaces.size()) {}
+
+void DeprecatedCamera3StreamSplitter::BufferTracker::mergeFence(const sp<Fence>& with) {
+ mMergedFence = Fence::merge(String8("DeprecatedCamera3StreamSplitter"), mMergedFence, with);
+}
+
+size_t DeprecatedCamera3StreamSplitter::BufferTracker::decrementReferenceCountLocked(
+ size_t surfaceId) {
+ const auto& it = std::find(mRequestedSurfaces.begin(), mRequestedSurfaces.end(), surfaceId);
+ if (it == mRequestedSurfaces.end()) {
+ return mReferenceCount;
+ } else {
+ mRequestedSurfaces.erase(it);
+ }
+
+ if (mReferenceCount > 0) --mReferenceCount;
+ return mReferenceCount;
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.h b/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.h
new file mode 100644
index 0000000..4610985
--- /dev/null
+++ b/services/camera/libcameraservice/device3/deprecated/DeprecatedCamera3StreamSplitter.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2014,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_STREAMSPLITTER_H
+#define ANDROID_SERVERS_STREAMSPLITTER_H
+
+#include <unordered_set>
+
+#include <camera/CameraMetadata.h>
+
+#include <gui/BufferItemConsumer.h>
+#include <gui/IConsumerListener.h>
+#include <gui/Surface.h>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#define SP_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.c_str(), ##__VA_ARGS__)
+#define SP_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.c_str(), ##__VA_ARGS__)
+#define SP_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.c_str(), ##__VA_ARGS__)
+#define SP_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.c_str(), ##__VA_ARGS__)
+
+namespace android {
+
+class GraphicBuffer;
+class IGraphicBufferConsumer;
+class IGraphicBufferProducer;
+
+// DeprecatedCamera3StreamSplitter is an autonomous class that manages one input BufferQueue
+// and multiple output BufferQueues. By using the buffer attach and detach logic
+// in BufferQueue, it is able to present the illusion of a single split
+// BufferQueue, where each buffer queued to the input is available to be
+// acquired by each of the outputs, and is able to be dequeued by the input
+// again only once all of the outputs have released it.
+class DeprecatedCamera3StreamSplitter : public BnConsumerListener {
+ public:
+ // Constructor
+ DeprecatedCamera3StreamSplitter(bool useHalBufManager = false);
+
+ // Connect to the stream splitter by creating buffer queue and connecting it
+ // with output surfaces.
+ status_t connect(const std::unordered_map<size_t, sp<Surface>>& surfaces,
+ uint64_t consumerUsage, uint64_t producerUsage, size_t halMaxBuffers,
+ uint32_t width, uint32_t height, android::PixelFormat format,
+ sp<Surface>* consumer, int64_t dynamicRangeProfile);
+
+ // addOutput adds an output BufferQueue to the splitter. The splitter
+ // connects to outputQueue as a CPU producer, and any buffers queued
+ // to the input will be queued to each output. If any output is abandoned
+ // by its consumer, the splitter will abandon its input queue (see onAbandoned).
+ //
+ // A return value other than NO_ERROR means that an error has occurred and
+ // outputQueue has not been added to the splitter. BAD_VALUE is returned if
+ // outputQueue is NULL. See IGraphicBufferProducer::connect for explanations
+ // of other error codes.
+ status_t addOutput(size_t surfaceId, const sp<Surface>& outputQueue);
+
+ // removeOutput will remove a BufferQueue that was previously added to
+ // the splitter outputs. Any pending buffers in the BufferQueue will get
+ // reclaimed.
+ status_t removeOutput(size_t surfaceId);
+
+ // Notification that the graphic buffer has been released to the input
+ // BufferQueue. The buffer should be reused by the camera device instead of
+ // queuing to the outputs.
+ status_t notifyBufferReleased(const sp<GraphicBuffer>& buffer);
+
+ // Attach a buffer to the specified outputs. This call reserves a buffer
+ // slot in the output queue.
+ status_t attachBufferToOutputs(ANativeWindowBuffer* anb,
+ const std::vector<size_t>& surface_ids);
+
+ // Get return value of onFrameAvailable to work around problem that
+ // onFrameAvailable is void. This function should be called by the producer
+ // right after calling queueBuffer().
+ status_t getOnFrameAvailableResult();
+
+ // Disconnect the buffer queue from output surfaces.
+ void disconnect();
+
+ void setHalBufferManager(bool enabled);
+
+ private:
+ // From IConsumerListener
+ //
+ // During this callback, we store some tracking information, detach the
+ // buffer from the input, and attach it to each of the outputs. This call
+ // can block if there are too many outstanding buffers. If it blocks, it
+ // will resume when onBufferReleasedByOutput releases a buffer back to the
+ // input.
+ void onFrameAvailable(const BufferItem& item) override;
+
+ // From IConsumerListener
+ //
+ // Similar to onFrameAvailable, but buffer item is indeed replacing a buffer
+ // in the buffer queue. This can happen when buffer queue is in droppable
+ // mode.
+ void onFrameReplaced(const BufferItem& item) override;
+
+ // From IConsumerListener
+ // We don't care about released buffers because we detach each buffer as
+ // soon as we acquire it. See the comment for onBufferReleased below for
+ // some clarifying notes about the name.
+ void onBuffersReleased() override {}
+
+ // From IConsumerListener
+ // We don't care about sideband streams, since we won't be splitting them
+ void onSidebandStreamChanged() override {}
+
+ // This is the implementation of the onBufferReleased callback from
+ // IProducerListener. It gets called from an OutputListener (see below), and
+ // 'from' is which producer interface from which the callback was received.
+ //
+ // During this callback, we detach the buffer from the output queue that
+ // generated the callback, update our state tracking to see if this is the
+ // last output releasing the buffer, and if so, release it to the input.
+ // If we release the buffer to the input, we allow a blocked
+ // onFrameAvailable call to proceed.
+ void onBufferReleasedByOutput(const sp<IGraphicBufferProducer>& from);
+
+ // Called by outputBufferLocked when a buffer in the async buffer queue got replaced.
+ void onBufferReplacedLocked(const sp<IGraphicBufferProducer>& from, size_t surfaceId);
+
+ // When this is called, the splitter disconnects from (i.e., abandons) its
+ // input queue and signals any waiting onFrameAvailable calls to wake up.
+ // It still processes callbacks from other outputs, but only detaches their
+ // buffers so they can continue operating until they run out of buffers to
+ // acquire. This must be called with mMutex locked.
+ void onAbandonedLocked();
+
+ // Decrement the buffer's reference count. Once the reference count becomes
+ // 0, return the buffer back to the input BufferQueue.
+ void decrementBufRefCountLocked(uint64_t id, size_t surfaceId);
+
+ // Check for and handle any output surface dequeue errors.
+ void handleOutputDequeueStatusLocked(status_t res, int slot);
+
+ // Handles released output surface buffers.
+ void returnOutputBufferLocked(const sp<Fence>& fence, const sp<IGraphicBufferProducer>& from,
+ size_t surfaceId, int slot);
+
+ // This is a thin wrapper class that lets us determine which BufferQueue
+ // the IProducerListener::onBufferReleased callback is associated with. We
+ // create one of these per output BufferQueue, and then pass the producer
+ // into onBufferReleasedByOutput above.
+ class OutputListener : public SurfaceListener, public IBinder::DeathRecipient {
+ public:
+ OutputListener(wp<DeprecatedCamera3StreamSplitter> splitter,
+ wp<IGraphicBufferProducer> output);
+ virtual ~OutputListener() = default;
+
+ // From IProducerListener
+ void onBufferReleased() override;
+ bool needsReleaseNotify() override { return true; };
+ void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {};
+ void onBufferDetached(int /*slot*/) override {}
+
+ // From IBinder::DeathRecipient
+ void binderDied(const wp<IBinder>& who) override;
+
+ private:
+ wp<DeprecatedCamera3StreamSplitter> mSplitter;
+ wp<IGraphicBufferProducer> mOutput;
+ };
+
+ class BufferTracker {
+ public:
+ BufferTracker(const sp<GraphicBuffer>& buffer,
+ const std::vector<size_t>& requestedSurfaces);
+ ~BufferTracker() = default;
+
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ const sp<Fence>& getMergedFence() const { return mMergedFence; }
+
+ void mergeFence(const sp<Fence>& with);
+
+ // Returns the new value
+ // Only called while mMutex is held
+ size_t decrementReferenceCountLocked(size_t surfaceId);
+
+ const std::vector<size_t> requestedSurfaces() const { return mRequestedSurfaces; }
+
+ private:
+ // Disallow copying
+ BufferTracker(const BufferTracker& other);
+ BufferTracker& operator=(const BufferTracker& other);
+
+ sp<GraphicBuffer> mBuffer; // One instance that holds this native handle
+ sp<Fence> mMergedFence;
+
+ // Request surfaces for a particular buffer. And when the buffer becomes
+ // available from the input queue, the registered surfaces are used to decide
+ // which output is the buffer sent to.
+ std::vector<size_t> mRequestedSurfaces;
+ size_t mReferenceCount;
+ };
+
+ // Must be accessed through RefBase
+ virtual ~DeprecatedCamera3StreamSplitter();
+
+ status_t addOutputLocked(size_t surfaceId, const sp<Surface>& outputQueue);
+
+ status_t removeOutputLocked(size_t surfaceId);
+
+ // Send a buffer to particular output, and increment the reference count
+ // of the buffer. If this output is abandoned, the buffer's reference count
+ // won't be incremented.
+ status_t outputBufferLocked(const sp<IGraphicBufferProducer>& output,
+ const BufferItem& bufferItem, size_t surfaceId);
+
+ // Get unique name for the buffer queue consumer
+ std::string getUniqueConsumerName();
+
+ // Helper function to get the BufferQueue slot where a particular buffer is attached to.
+ int getSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp, const sp<GraphicBuffer>& gb);
+
+ // Sum of max consumer buffers for all outputs
+ size_t mMaxConsumerBuffers = 0;
+ size_t mMaxHalBuffers = 0;
+ uint32_t mWidth = 0;
+ uint32_t mHeight = 0;
+ android::PixelFormat mFormat = android::PIXEL_FORMAT_NONE;
+ uint64_t mProducerUsage = 0;
+ int mDynamicRangeProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD;
+
+ // The attachBuffer call will happen on different thread according to mUseHalBufManager and have
+ // different timing constraint.
+ static const nsecs_t kNormalDequeueBufferTimeout = s2ns(1); // 1 sec
+ static const nsecs_t kHalBufMgrDequeueBufferTimeout = ms2ns(1); // 1 msec
+
+ Mutex mMutex;
+
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<BufferItemConsumer> mBufferItemConsumer;
+ sp<Surface> mSurface;
+
+ // Map graphic buffer ids -> buffer items
+ std::unordered_map<uint64_t, BufferItem> mInputSlots;
+
+ // Map surface ids -> gbp outputs
+ std::unordered_map<int, sp<IGraphicBufferProducer>> mOutputs;
+
+ // Map surface ids -> gbp outputs
+ std::unordered_map<int, sp<Surface>> mOutputSurfaces;
+
+ // Map surface ids -> consumer buffer count
+ std::unordered_map<int, size_t> mConsumerBufferCount;
+
+ // Map of GraphicBuffer IDs (GraphicBuffer::getId()) to buffer tracking
+ // objects (which are mostly for counting how many outputs have released the
+ // buffer, but also contain merged release fences).
+ std::unordered_map<uint64_t, std::unique_ptr<BufferTracker>> mBuffers;
+
+ struct GBPHash {
+ std::size_t operator()(const sp<IGraphicBufferProducer>& producer) const {
+ return std::hash<IGraphicBufferProducer*>{}(producer.get());
+ }
+ };
+
+ std::unordered_map<sp<IGraphicBufferProducer>, sp<OutputListener>, GBPHash> mNotifiers;
+
+ typedef std::vector<sp<GraphicBuffer>> OutputSlots;
+ std::unordered_map<sp<IGraphicBufferProducer>, std::unique_ptr<OutputSlots>, GBPHash>
+ mOutputSlots;
+
+ // A set of buffers that could potentially stay in some of the outputs after removal
+ // and therefore should be detached from the input queue.
+ std::unordered_set<uint64_t> mDetachedBuffers;
+
+ // Latest onFrameAvailable return value
+ std::atomic<status_t> mOnFrameAvailableRes{0};
+
+ // Currently acquired input buffers
+ size_t mAcquiredInputBuffers;
+
+ std::string mConsumerName;
+
+ bool mUseHalBufManager;
+};
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/tests/Camera3StreamSplitterTest.cpp b/services/camera/libcameraservice/tests/Camera3StreamSplitterTest.cpp
index 3d49ae5..5e32482 100644
--- a/services/camera/libcameraservice/tests/Camera3StreamSplitterTest.cpp
+++ b/services/camera/libcameraservice/tests/Camera3StreamSplitterTest.cpp
@@ -17,10 +17,9 @@
#define LOG_TAG "Camera3StreamSplitterTest"
// #define LOG_NDEBUG 0
-#include "../device3/Camera3StreamSplitter.h"
-
#include <android/hardware_buffer.h>
#include <com_android_graphics_libgui_flags.h>
+#include <com_android_internal_camera_flags.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
@@ -35,6 +34,14 @@
#include <gtest/gtest.h>
+#include "../device3/Flags.h"
+
+#if USE_NEW_STREAM_SPLITTER
+#include "../device3/Camera3StreamSplitter.h"
+#else
+#include "../device3/deprecated/DeprecatedCamera3StreamSplitter.h"
+#endif // USE_NEW_STREAM_SPLITTER
+
using namespace android;
namespace {
@@ -62,10 +69,20 @@
class Camera3StreamSplitterTest : public testing::Test {
public:
- void SetUp() override { mSplitter = sp<Camera3StreamSplitter>::make(); }
+ void SetUp() override {
+#if USE_NEW_STREAM_SPLITTER
+ mSplitter = sp<Camera3StreamSplitter>::make();
+#else
+ mSplitter = sp<DeprecatedCamera3StreamSplitter>::make();
+#endif // USE_NEW_STREAM_SPLITTER
+ }
protected:
+#if USE_NEW_STREAM_SPLITTER
sp<Camera3StreamSplitter> mSplitter;
+#else
+ sp<DeprecatedCamera3StreamSplitter> mSplitter;
+#endif // USE_NEW_STREAM_SPLITTER
};
class TestSurfaceListener : public SurfaceListener {
@@ -102,7 +119,7 @@
} // namespace
-TEST_F(Camera3StreamSplitterTest, TestWithoutSurfaces_NoBuffersConsumed) {
+TEST_F(Camera3StreamSplitterTest, WithoutSurfaces_NoBuffersConsumed) {
sp<Surface> consumer;
EXPECT_EQ(OK, mSplitter->connect({}, kConsumerUsage, kProducerUsage, kHalMaxBuffers, kWidth,
kHeight, kFormat, &consumer, kDynamicRangeProfile));