Merge f30357450bd04c2898bb926e86b911316e5d587e on remote branch
Change-Id: I938e1d59ab9168275cd66d00500278ce4fd1a7d7
diff --git a/components/Android.bp b/components/Android.bp
index 19dc794..5e4e45d 100644
--- a/components/Android.bp
+++ b/components/Android.bp
@@ -22,11 +22,13 @@
shared_libs: [
"android.hardware.graphics.common@1.0",
+ "libc2plugin_store",
"libchrome",
"libcodec2_soft_common",
"libcutils",
"liblog",
"libsfplugin_ccodec_utils",
+ "libstagefright_bufferqueue_helper",
"libstagefright_foundation",
"libv4l2_codec2_store",
],
@@ -39,5 +41,6 @@
"-Werror",
"-Wall",
"-Wno-unused-parameter", // needed for libchrome/base codes
+ "-Wthread-safety",
],
}
diff --git a/components/V4L2DecodeComponent.cpp b/components/V4L2DecodeComponent.cpp
index 4c657e5..8a86466 100644
--- a/components/V4L2DecodeComponent.cpp
+++ b/components/V4L2DecodeComponent.cpp
@@ -27,6 +27,7 @@
#include <v4l2_codec2/components/V4L2Decoder.h>
#include <v4l2_codec2/components/VideoFramePool.h>
#include <v4l2_codec2/components/VideoTypes.h>
+#include <v4l2_codec2/plugin_store/C2VdaBqBlockPool.h>
namespace android {
namespace {
@@ -170,8 +171,6 @@
ALOGV("%s(%s)", __func__, name.c_str());
mIsSecure = name.find(".secure") != std::string::npos;
- // TODO(b/153608694): Support secure mode.
- ALOG_ASSERT(!mIsSecure, "Secure mode is not supported yet.");
}
V4L2DecodeComponent::~V4L2DecodeComponent() {
@@ -259,7 +258,8 @@
}
void V4L2DecodeComponent::getVideoFramePool(std::unique_ptr<VideoFramePool>* pool,
- const media::Size& size, HalPixelFormat pixelFormat) {
+ const media::Size& size, HalPixelFormat pixelFormat,
+ size_t numBuffers) {
ALOGV("%s()", __func__);
ALOG_ASSERT(mDecoderTaskRunner->RunsTasksInCurrentSequence());
@@ -275,6 +275,11 @@
return;
}
+ // TODO(b/160307705): Consider to remove the dependency of C2VdaBqBlockPool.
+ if (blockPool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+ reinterpret_cast<C2VdaBqBlockPool*>(blockPool.get())->requestNewBufferSet(numBuffers);
+ }
+
*pool = VideoFramePool::Create(std::move(blockPool), size, pixelFormat, mIsSecure,
mDecoderTaskRunner);
}
@@ -522,6 +527,9 @@
C2Work* work = it->second.get();
C2ConstGraphicBlock constBlock = std::move(frame)->getGraphicBlock();
+ // TODO(b/160307705): Consider to remove the dependency of C2VdaBqBlockPool.
+ MarkBlockPoolDataAsShared(constBlock);
+
std::shared_ptr<C2Buffer> buffer = C2Buffer::CreateGraphicBuffer(std::move(constBlock));
if (mPendingColorAspectsChange &&
work->input.ordinal.frameIndex.peeku() >= mPendingColorAspectsChangeFrameIndex) {
diff --git a/components/V4L2DecodeInterface.cpp b/components/V4L2DecodeInterface.cpp
index 487a70f..f975e95 100644
--- a/components/V4L2DecodeInterface.cpp
+++ b/components/V4L2DecodeInterface.cpp
@@ -14,6 +14,7 @@
#include <media/stagefright/foundation/MediaDefs.h>
#include <v4l2_codec2/common/V4L2ComponentCommon.h>
+#include <v4l2_codec2/plugin_store/V4L2AllocatorId.h>
#include <v4l2_device.h>
namespace android {
@@ -260,10 +261,13 @@
.calculatedAs(MaxInputBufferSizeCalculator, mSize)
.build());
- // TODO(b/153608694): Support secure mode.
- const C2Allocator::id_t inputAllocators[] = {C2PlatformAllocatorStore::BLOB};
+ bool secureMode = name.find(".secure") != std::string::npos;
+ const C2Allocator::id_t inputAllocators[] = {secureMode ? V4L2AllocatorId::SECURE_LINEAR
+ : C2PlatformAllocatorStore::BLOB};
+
const C2Allocator::id_t outputAllocators[] = {C2AllocatorStore::DEFAULT_GRAPHIC};
- const C2Allocator::id_t surfaceAllocator = C2PlatformAllocatorStore::BUFFERQUEUE;
+ const C2Allocator::id_t surfaceAllocator =
+ secureMode ? V4L2AllocatorId::SECURE_GRAPHIC : V4L2AllocatorId::V4L2_BUFFERQUEUE;
const C2BlockPool::local_id_t outputBlockPools[] = {C2BlockPool::BASIC_GRAPHIC};
addParameter(
diff --git a/components/V4L2Decoder.cpp b/components/V4L2Decoder.cpp
index 6a60dda..275357e 100644
--- a/components/V4L2Decoder.cpp
+++ b/components/V4L2Decoder.cpp
@@ -479,7 +479,7 @@
}
// Always use fexible pixel 420 format YCBCR_420_888 in Android.
- mGetPoolCb.Run(&mVideoFramePool, mCodedSize, HalPixelFormat::YCBCR_420_888);
+ mGetPoolCb.Run(&mVideoFramePool, mCodedSize, HalPixelFormat::YCBCR_420_888, *numOutputBuffers);
if (!mVideoFramePool) {
ALOGE("Failed to get block pool with size: %s", mCodedSize.ToString().c_str());
return false;
diff --git a/components/include/v4l2_codec2/components/V4L2DecodeComponent.h b/components/include/v4l2_codec2/components/V4L2DecodeComponent.h
index 0def53c..37da866 100644
--- a/components/include/v4l2_codec2/components/V4L2DecodeComponent.h
+++ b/components/include/v4l2_codec2/components/V4L2DecodeComponent.h
@@ -71,7 +71,7 @@
void pumpPendingWorks();
// Get the buffer pool.
void getVideoFramePool(std::unique_ptr<VideoFramePool>* pool, const media::Size& size,
- HalPixelFormat pixelFormat);
+ HalPixelFormat pixelFormat, size_t numBuffers);
// Detect and report works with no-show frame, only used at VP8 and VP9.
void detectNoShowFrameWorksAndReportIfFinished(const C2WorkOrdinalStruct& currOrdinal);
diff --git a/components/include/v4l2_codec2/components/VideoDecoder.h b/components/include/v4l2_codec2/components/VideoDecoder.h
index 9616106..41517b3 100644
--- a/components/include/v4l2_codec2/components/VideoDecoder.h
+++ b/components/include/v4l2_codec2/components/VideoDecoder.h
@@ -38,8 +38,9 @@
const size_t size;
};
- using GetPoolCB = base::RepeatingCallback<void(
- std::unique_ptr<VideoFramePool>*, const media::Size& size, HalPixelFormat pixelFormat)>;
+ using GetPoolCB =
+ base::RepeatingCallback<void(std::unique_ptr<VideoFramePool>*, const media::Size& size,
+ HalPixelFormat pixelFormat, size_t numOutputBuffers)>;
using DecodeCB = base::OnceCallback<void(DecodeStatus)>;
using OutputCB = base::RepeatingCallback<void(std::unique_ptr<VideoFrame>)>;
using ErrorCB = base::RepeatingCallback<void()>;
diff --git a/plugin_store/Android.bp b/plugin_store/Android.bp
new file mode 100644
index 0000000..3d3d040
--- /dev/null
+++ b/plugin_store/Android.bp
@@ -0,0 +1,37 @@
+cc_library_shared {
+ name: "libc2plugin_store",
+ vendor: true,
+
+ defaults: [
+ "libcodec2-impl-defaults",
+ ],
+
+ srcs: [
+ "C2VdaBqBlockPool.cpp",
+ "V4L2PluginStore.cpp",
+ "VendorAllocatorLoader.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+
+ header_libs: [
+ "libcodec2_internal",
+ ],
+ shared_libs: [
+ "android.hardware.graphics.bufferqueue@2.0",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "libnativewindow",
+ "liblog",
+ "libstagefright_bufferqueue_helper",
+ "libstagefright_foundation",
+ "libui",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/plugin_store/C2VdaBqBlockPool.cpp b/plugin_store/C2VdaBqBlockPool.cpp
new file mode 100644
index 0000000..94dce41
--- /dev/null
+++ b/plugin_store/C2VdaBqBlockPool.cpp
@@ -0,0 +1,1091 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2VdaBqBlockPool"
+
+#include <v4l2_codec2/plugin_store/C2VdaBqBlockPool.h>
+
+#include <errno.h>
+
+#include <chrono>
+#include <mutex>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <log/log.h>
+#include <system/window.h>
+#include <types.h>
+#include <ui/BufferQueueDefs.h>
+
+#include <v4l2_codec2/plugin_store/V4L2AllocatorId.h>
+
+using ::android::C2AndroidMemoryUsage;
+using ::android::Fence;
+using ::android::GraphicBuffer;
+using ::android::sp;
+using ::android::status_t;
+using ::android::BufferQueueDefs::BUFFER_NEEDS_REALLOCATION;
+using ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::Return;
+
+using HBuffer = ::android::hardware::graphics::common::V1_2::HardwareBuffer;
+using HStatus = ::android::hardware::graphics::bufferqueue::V2_0::Status;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::b2h;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::h2b;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::HFenceWrapper;
+
+namespace {
+
+// The wait time for acquire fence in milliseconds.
+constexpr int kFenceWaitTimeMs = 10;
+// The timeout delay range for dequeuing spare buffer delay time in microseconds.
+constexpr int kDequeueSpareMinDelayUs = 500;
+constexpr int kDequeueSpareMaxDelayUs = 16 * 1000;
+// The timeout limit of acquiring lock of timed_mutex in milliseconds.
+constexpr std::chrono::milliseconds kTimedMutexTimeoutMs = std::chrono::milliseconds(500);
+// The max retry times for fetchSpareBufferSlot timeout.
+constexpr int32_t kFetchSpareBufferMaxRetries = 10;
+
+} // namespace
+
+static c2_status_t asC2Error(int32_t err) {
+ switch (err) {
+ case android::NO_ERROR:
+ return C2_OK;
+ case android::NO_INIT:
+ return C2_NO_INIT;
+ case android::BAD_VALUE:
+ return C2_BAD_VALUE;
+ case android::TIMED_OUT:
+ return C2_TIMED_OUT;
+ case android::WOULD_BLOCK:
+ return C2_BLOCKING;
+ case android::NO_MEMORY:
+ return C2_NO_MEMORY;
+ }
+ return C2_CORRUPTED;
+}
+
+/**
+ * BlockPoolData implementation for C2VdaBqBlockPool. The life cycle of this object should be as
+ * long as its accompanied C2GraphicBlock.
+ *
+ * When C2VdaBqBlockPoolData is created, |mShared| is false, and the owner of the accompanied
+ * C2GraphicBlock is the component that called fetchGraphicBlock(). If this is released before
+ * sharing, the destructor will call detachBuffer() to BufferQueue to free the slot.
+ *
+ * When the accompanied C2GraphicBlock is going to share to client from component, component should
+ * call MarkBlockPoolDataAsShared() to set |mShared| to true, and then this will be released after
+ * the transition of C2GraphicBlock across HIDL interface. At this time, the destructor will not
+ * call detachBuffer().
+ */
+struct C2VdaBqBlockPoolData : public _C2BlockPoolData {
+ // This type should be a different value than what _C2BlockPoolData::type_t has defined.
+ static constexpr int kTypeVdaBufferQueue = TYPE_BUFFERQUEUE + 256;
+
+ C2VdaBqBlockPoolData(uint64_t producerId, int32_t slotId,
+ const std::shared_ptr<C2VdaBqBlockPool::Impl>& pool);
+ C2VdaBqBlockPoolData() = delete;
+
+ // If |mShared| is false, call detach buffer to BufferQueue via |mPool|
+ virtual ~C2VdaBqBlockPoolData() override;
+
+ type_t getType() const override { return static_cast<type_t>(kTypeVdaBufferQueue); }
+
+ bool mShared = false; // whether is shared from component to client.
+ const uint64_t mProducerId;
+ const int32_t mSlotId;
+ const std::shared_ptr<C2VdaBqBlockPool::Impl> mPool;
+};
+
+c2_status_t MarkBlockPoolDataAsShared(const C2ConstGraphicBlock& sharedBlock) {
+ std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(sharedBlock);
+ if (!data || data->getType() != C2VdaBqBlockPoolData::kTypeVdaBufferQueue) {
+ // Skip this functtion if |sharedBlock| is not fetched from C2VdaBqBlockPool.
+ return C2_OMITTED;
+ }
+ const std::shared_ptr<C2VdaBqBlockPoolData> poolData =
+ std::static_pointer_cast<C2VdaBqBlockPoolData>(data);
+ if (poolData->mShared) {
+ ALOGE("C2VdaBqBlockPoolData(id=%" PRIu64 ", slot=%d) is already marked as shared...",
+ poolData->mProducerId, poolData->mSlotId);
+ return C2_BAD_STATE;
+ }
+ poolData->mShared = true;
+ return C2_OK;
+}
+
+// static
+c2_status_t C2VdaBqBlockPool::getPoolIdFromGraphicBlock(
+ const std::shared_ptr<C2GraphicBlock>& block, uint32_t* poolId) {
+ uint32_t width, height, format, stride, igbp_slot, generation;
+ uint64_t usage, igbp_id;
+ android::_UnwrapNativeCodec2GrallocMetadata(block->handle(), &width, &height, &format, &usage,
+ &stride, &generation, &igbp_id, &igbp_slot);
+ ALOGV("Unwrap Metadata: igbp[%" PRIu64 ", %u] (%u*%u, fmt %#x, usage %" PRIx64 ", stride %u)",
+ igbp_id, igbp_slot, width, height, format, usage, stride);
+ *poolId = igbp_slot;
+ return C2_OK;
+}
+
+class C2VdaBqBlockPool::Impl : public std::enable_shared_from_this<C2VdaBqBlockPool::Impl> {
+public:
+ using HGraphicBufferProducer = C2VdaBqBlockPool::HGraphicBufferProducer;
+
+ explicit Impl(const std::shared_ptr<C2Allocator>& allocator);
+ // TODO: should we detach buffers on producer if any on destructor?
+ ~Impl() = default;
+
+ c2_status_t fetchGraphicBlock(uint32_t width, uint32_t height, uint32_t format,
+ C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicBlock>* block /* nonnull */);
+ void setRenderCallback(const C2BufferQueueBlockPool::OnRenderCallback& renderCallback);
+ void configureProducer(const sp<HGraphicBufferProducer>& producer);
+ c2_status_t requestNewBufferSet(int32_t bufferCount);
+ c2_status_t updateGraphicBlock(bool willCancel, uint32_t oldSlot, uint32_t* newSlot,
+ std::shared_ptr<C2GraphicBlock>* block /* nonnull */);
+ c2_status_t getMinBuffersForDisplay(size_t* bufferCount);
+
+private:
+ friend struct C2VdaBqBlockPoolData;
+
+ // The exponential rate control calculator with factor of 2. Per increase() call will double the
+ // value until it reaches maximum. reset() will set value to the minimum.
+ class ExpRateControlCalculator {
+ public:
+ ExpRateControlCalculator(int min, int max) : kMinValue(min), kMaxValue(max), mValue(min) {}
+ ExpRateControlCalculator() = delete;
+
+ void reset() { mValue = kMinValue; }
+ void increase() { mValue = std::min(kMaxValue, mValue << 1); }
+ int value() const { return mValue; }
+
+ private:
+ const int kMinValue;
+ const int kMaxValue;
+ int mValue;
+ };
+
+ // Requested buffer formats.
+ struct BufferFormat {
+ BufferFormat(uint32_t width, uint32_t height, uint32_t pixelFormat,
+ C2AndroidMemoryUsage androidUsage)
+ : mWidth(width), mHeight(height), mPixelFormat(pixelFormat), mUsage(androidUsage) {}
+ BufferFormat() = default;
+
+ uint32_t mWidth = 0;
+ uint32_t mHeight = 0;
+ uint32_t mPixelFormat = 0;
+ C2AndroidMemoryUsage mUsage = C2MemoryUsage(0);
+ };
+
+ // For C2VdaBqBlockPoolData to detach corresponding slot buffer from BufferQueue.
+ void detachBuffer(uint64_t producerId, int32_t slotId);
+
+ // Fetches a spare slot index by dequeueing and requesting one extra buffer from producer. The
+ // spare buffer slot guarantees at least one buffer to be dequeued in producer, so as to prevent
+ // the invalid operation for producer of the attempt to dequeue buffers exceeded the maximal
+ // dequeued buffer count.
+ // This function should be called after the last requested buffer is fetched in
+ // fetchGraphicBlock(), or in the beginning of switchProducer(). Block pool should store the
+ // slot index into |mSpareSlot| and cancel the buffer immediately.
+ // The generation number and usage of the spare buffer will be recorded in |generation| and
+ // |usage|, which will be useful later in switchProducer().
+ c2_status_t fetchSpareBufferSlot(HGraphicBufferProducer* const producer, uint32_t width,
+ uint32_t height, uint32_t pixelFormat,
+ C2AndroidMemoryUsage androidUsage, uint32_t* generation,
+ uint64_t* usage);
+
+ // Helper function to call dequeue buffer to producer.
+ c2_status_t dequeueBuffer(HGraphicBufferProducer* const producer, uint32_t width,
+ uint32_t height, uint32_t pixelFormat,
+ C2AndroidMemoryUsage androidUsage, int32_t& status, int32_t& slot,
+ sp<Fence>& fence);
+
+ // Switches producer and transfers allocated buffers from old producer to the new one.
+ bool switchProducer(HGraphicBufferProducer* const newProducer, uint64_t newProducerId);
+
+ const std::shared_ptr<C2Allocator> mAllocator;
+
+ sp<HGraphicBufferProducer> mProducer;
+ uint64_t mProducerId;
+ C2BufferQueueBlockPool::OnRenderCallback mRenderCallback;
+
+ // Function mutex to lock at the start of each API function call for protecting the
+ // synchronization of all member variables.
+ std::mutex mMutex;
+ // The mutex of excluding the procedures of configuring producer and allocating buffers. They
+ // should be blocked mutually. Set the timeout for acquiring lock in case of any deadlock.
+ // Configuring producer: configureProducer() called by CCodec.
+ // Allocating buffers: requestNewBufferSet(), then a loop of fetchGraphicBlock() called by
+ // compoenent until |mSlotAllocations|.size() equals |mBuffersRequested|.
+ std::timed_mutex mConfigureProducerAndAllocateBuffersMutex;
+ // The unique lock of the procedure of allocating buffers. It should be locked in the beginning
+ // of requestNewBufferSet() and unlock in the end of the loop of fetchGraphicBlock(). Note that
+ // all calls should be in the same thread.
+ std::unique_lock<std::timed_mutex> mAllocateBuffersLock;
+
+ // The map restored C2GraphicAllocation from corresponding slot index.
+ std::map<int32_t, std::shared_ptr<C2GraphicAllocation>> mSlotAllocations;
+ // Number of buffers requested on requestNewBufferSet() call.
+ size_t mBuffersRequested;
+ // The slot index of spare buffer.
+ int32_t mSpareSlot;
+ // Currently requested buffer formats.
+ BufferFormat mBufferFormat;
+ // The map recorded the slot indices from old producer to new producer.
+ std::map<int32_t, int32_t> mProducerChangeSlotMap;
+ // The rate control calculator for the delay of dequeueing spare buffer.
+ ExpRateControlCalculator mSpareDequeueDelayUs;
+ // The counter for representing the buffer count in client. Only used in producer switching
+ // case. It will be reset in switchProducer(), and accumulated in updateGraphicBlock() routine.
+ uint32_t mBuffersInClient = 0u;
+ // The indicator to record if producer has been switched. Set to true when producer is switched.
+ // Toggle off when requestNewBufferSet() is called. We forcedly detach all slots to make sure
+ // all slots are available, except the ones owned by client.
+ bool mProducerSwitched = false;
+};
+
+C2VdaBqBlockPool::Impl::Impl(const std::shared_ptr<C2Allocator>& allocator)
+ : mAllocator(allocator),
+ mAllocateBuffersLock(mConfigureProducerAndAllocateBuffersMutex, std::defer_lock),
+ mBuffersRequested(0u),
+ mSpareSlot(-1),
+ mSpareDequeueDelayUs(kDequeueSpareMinDelayUs, kDequeueSpareMaxDelayUs) {}
+
+c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlock(
+ uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mProducer) {
+ // Producer will not be configured in byte-buffer mode. Allocate buffers from allocator
+ // directly as a basic graphic block pool.
+ std::shared_ptr<C2GraphicAllocation> alloc;
+ c2_status_t err = mAllocator->newGraphicAllocation(width, height, format, usage, &alloc);
+ if (err != C2_OK) {
+ return err;
+ }
+ *block = _C2BlockFactory::CreateGraphicBlock(alloc);
+ return C2_OK;
+ }
+
+ // The existence of |mProducerChangeSlotMap| indicates producer is just switched. Use return
+ // code C2_BAD_STATE to inform the component to handle the procedure of producer change.
+ // TODO(johnylin): consider to inform producer change to component in an active way.
+ if (!mProducerChangeSlotMap.empty()) {
+ return C2_BAD_STATE;
+ }
+
+ sp<Fence> fence = new Fence();
+ C2AndroidMemoryUsage androidUsage = usage;
+ int32_t status;
+ uint32_t pixelFormat = format;
+ int32_t slot;
+
+ c2_status_t err = dequeueBuffer(mProducer.get(), width, height, pixelFormat, androidUsage,
+ status, slot, fence);
+ if (err != C2_OK) {
+ return err;
+ }
+
+ // Wait for acquire fence if we get one.
+ HFenceWrapper hFenceWrapper{};
+ if (!b2h(fence, &hFenceWrapper)) {
+ ALOGE("Invalid fence received from dequeueBuffer.");
+ return C2_BAD_VALUE;
+ }
+ if (fence) {
+ status_t fenceStatus = fence->wait(kFenceWaitTimeMs);
+ if (fenceStatus != android::NO_ERROR) {
+ Return<HStatus> cancelTransStatus =
+ mProducer->cancelBuffer(slot, hFenceWrapper.getHandle());
+ if (!cancelTransStatus.isOk()) {
+ ALOGE("cancelBuffer transaction error: %s",
+ cancelTransStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ if (fenceStatus == -ETIME) { // fence wait timed out
+ ALOGV("buffer fence wait timed out, wait for retry...");
+ return C2_TIMED_OUT;
+ }
+ ALOGE("buffer fence wait error: %d", fenceStatus);
+ return asC2Error(fenceStatus);
+ }
+ if (mRenderCallback) {
+ nsecs_t signalTime = fence->getSignalTime();
+ if (signalTime >= 0 && signalTime < INT64_MAX) {
+ mRenderCallback(mProducerId, slot, signalTime);
+ } else {
+ ALOGV("got fence signal time of %" PRId64 " nsec", signalTime);
+ }
+ }
+ }
+
+ auto iter = mSlotAllocations.find(slot);
+ if (iter == mSlotAllocations.end()) {
+ if (slot == mSpareSlot) {
+ // The dequeued slot is the spare buffer, we don't use this buffer for decoding and must
+ // cancel it after the delay time. Other working buffers may be available and pushed to
+ // free buffer queue in producer during the delay.
+ ALOGV("dequeued spare slot, cancel it after a wait time delay (%d)...",
+ mSpareDequeueDelayUs.value());
+ ::usleep(mSpareDequeueDelayUs.value()); // wait for retry
+ // Double the delay time if spare buffer still be dequeued the next time. This could
+ // prevent block pool keeps aggressively dequeueing spare buffer while other buffers are
+ // not available yet.
+ mSpareDequeueDelayUs.increase();
+ Return<HStatus> cancelTransStatus =
+ mProducer->cancelBuffer(slot, hFenceWrapper.getHandle());
+ if (!cancelTransStatus.isOk()) {
+ ALOGE("cancelBuffer transaction error: %s",
+ cancelTransStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ return C2_TIMED_OUT;
+ }
+ if (mSlotAllocations.size() >= mBuffersRequested) {
+ // The dequeued slot has a pre-allocated buffer whose size and format is as same as
+ // currently requested (but was not dequeued during allocation cycle). Just detach it to
+ // free this slot. And try dequeueBuffer again.
+ ALOGD("dequeued a new slot index but already allocated enough buffers. Detach it.");
+ Return<HStatus> detachTransStatus = mProducer->detachBuffer(slot);
+ if (!detachTransStatus.isOk()) {
+ ALOGE("detachBuffer transaction error: %s",
+ detachTransStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ return C2_TIMED_OUT;
+ }
+ if (status != BUFFER_NEEDS_REALLOCATION) {
+ // The dequeued slot has a pre-allocated buffer whose size and format is as same as
+ // currently requested, so there is no BUFFER_NEEDS_REALLOCATION flag. However since the
+ // buffer reference is already dropped, still call requestBuffer to re-allocate then.
+ // Add a debug note here for tracking.
+ ALOGD("dequeued a new slot index without BUFFER_NEEDS_REALLOCATION flag.");
+ }
+
+ // Call requestBuffer to allocate buffer for the slot and obtain the reference.
+ sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
+ uint32_t generation;
+ Return<void> transStatus = mProducer->requestBuffer(
+ slot, [&status, &slotBuffer, &generation](HStatus hStatus, HBuffer const& hBuffer,
+ uint32_t generationNumber) {
+ if (h2b(hStatus, &status) && h2b(hBuffer, &slotBuffer) && slotBuffer) {
+ generation = generationNumber;
+ slotBuffer->setGenerationNumber(generationNumber);
+ } else {
+ status = android::BAD_VALUE;
+ }
+ });
+
+ // Check requestBuffer transaction status
+ if (!transStatus.isOk()) {
+ ALOGE("requestBuffer transaction error: %s", transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ // Check requestBuffer return flag
+ if (status != android::NO_ERROR) {
+ ALOGE("requestBuffer failed: %d", status);
+ Return<HStatus> cancelTransStatus =
+ mProducer->cancelBuffer(slot, hFenceWrapper.getHandle());
+ if (!cancelTransStatus.isOk()) {
+ ALOGE("cancelBuffer transaction error: %s",
+ cancelTransStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ return asC2Error(status);
+ }
+
+ // Convert GraphicBuffer to C2GraphicAllocation and wrap producer id and slot index
+ ALOGV("buffer wraps { producer id: %" PRIu64 ", slot: %d }", mProducerId, slot);
+ C2Handle* c2Handle = android::WrapNativeCodec2GrallocHandle(
+ slotBuffer->handle, slotBuffer->width, slotBuffer->height, slotBuffer->format,
+ slotBuffer->usage, slotBuffer->stride, slotBuffer->getGenerationNumber(),
+ mProducerId, slot);
+ if (!c2Handle) {
+ ALOGE("WrapNativeCodec2GrallocHandle failed");
+ return C2_NO_MEMORY;
+ }
+
+ std::shared_ptr<C2GraphicAllocation> alloc;
+ c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
+ if (err != C2_OK) {
+ ALOGE("priorGraphicAllocation failed: %d", err);
+ return err;
+ }
+
+ mSlotAllocations[slot] = std::move(alloc);
+ if (mSlotAllocations.size() == mBuffersRequested) {
+ // Allocate one spare buffer after allocating enough buffers requested by client.
+ uint32_t generation;
+ uint64_t usage;
+
+ err = C2_TIMED_OUT;
+ for (int32_t retriesLeft = kFetchSpareBufferMaxRetries;
+ err == C2_TIMED_OUT && retriesLeft >= 0; retriesLeft--) {
+ err = fetchSpareBufferSlot(mProducer.get(), width, height, pixelFormat,
+ androidUsage, &generation, &usage);
+ }
+ if (err != C2_OK) {
+ ALOGE("fetchSpareBufferSlot failed after %d retries: %d",
+ kFetchSpareBufferMaxRetries, err);
+ return err;
+ }
+
+ // Already allocated enough buffers, set allowAllocation to false to restrict the
+ // eligible slots to allocated ones for future dequeue.
+ Return<HStatus> transStatus = mProducer->allowAllocation(false);
+ if (!transStatus.isOk()) {
+ ALOGE("allowAllocation(false) transaction error: %s",
+ transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ if (!h2b(static_cast<HStatus>(transStatus), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("allowAllocation(false) failed");
+ return asC2Error(status);
+ }
+ // Store buffer formats for future usage.
+ mBufferFormat = BufferFormat(width, height, pixelFormat, androidUsage);
+ ALOG_ASSERT(mAllocateBuffersLock.owns_lock());
+ mAllocateBuffersLock.unlock();
+ }
+ } else if (mSlotAllocations.size() < mBuffersRequested) {
+ ALOGE("failed to allocate enough buffers");
+ return C2_NO_MEMORY;
+ }
+
+ // Reset spare dequeue delay time once we have dequeued a working buffer.
+ mSpareDequeueDelayUs.reset();
+
+ auto poolData = std::make_shared<C2VdaBqBlockPoolData>(mProducerId, slot, shared_from_this());
+ *block = _C2BlockFactory::CreateGraphicBlock(mSlotAllocations[slot], std::move(poolData));
+ return C2_OK;
+}
+
+c2_status_t C2VdaBqBlockPool::Impl::fetchSpareBufferSlot(HGraphicBufferProducer* const producer,
+ uint32_t width, uint32_t height,
+ uint32_t pixelFormat,
+ C2AndroidMemoryUsage androidUsage,
+ uint32_t* generation, uint64_t* usage) {
+ ALOGV("fetchSpareBufferSlot");
+ sp<Fence> fence = new Fence();
+ int32_t status;
+ int32_t slot;
+
+ c2_status_t err =
+ dequeueBuffer(producer, width, height, pixelFormat, androidUsage, status, slot, fence);
+ if (err != C2_OK) {
+ return err;
+ }
+
+ // Wait for acquire fence if we get one.
+ HFenceWrapper hFenceWrapper{};
+ if (!b2h(fence, &hFenceWrapper)) {
+ ALOGE("Invalid fence received from dequeueBuffer.");
+ return C2_BAD_VALUE;
+ }
+ if (fence) {
+ status_t fenceStatus = fence->wait(kFenceWaitTimeMs);
+ if (fenceStatus != android::NO_ERROR) {
+ Return<HStatus> cancelTransStatus =
+ producer->cancelBuffer(slot, hFenceWrapper.getHandle());
+ if (!cancelTransStatus.isOk()) {
+ ALOGE("cancelBuffer transaction error: %s",
+ cancelTransStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ if (fenceStatus == -ETIME) { // fence wait timed out
+ ALOGV("buffer fence wait timed out, wait for retry...");
+ return C2_TIMED_OUT;
+ }
+ ALOGE("buffer fence wait error: %d", fenceStatus);
+ return asC2Error(fenceStatus);
+ }
+ }
+
+ if (status != BUFFER_NEEDS_REALLOCATION) {
+ ALOGD("dequeued a new slot index without BUFFER_NEEDS_REALLOCATION flag.");
+ }
+
+ // Call requestBuffer to allocate buffer for the slot and obtain the reference.
+ // Get generation number here.
+ sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
+ Return<void> transStatus = producer->requestBuffer(
+ slot, [&status, &slotBuffer, &generation](HStatus hStatus, HBuffer const& hBuffer,
+ uint32_t generationNumber) {
+ if (h2b(hStatus, &status) && h2b(hBuffer, &slotBuffer) && slotBuffer) {
+ *generation = generationNumber;
+ slotBuffer->setGenerationNumber(generationNumber);
+ } else {
+ status = android::BAD_VALUE;
+ }
+ });
+
+ // Check requestBuffer transaction status.
+ if (!transStatus.isOk()) {
+ ALOGE("requestBuffer transaction error: %s", transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+
+ // Get generation number and usage from the slot buffer.
+ *usage = slotBuffer->getUsage();
+ ALOGV("Obtained from spare buffer: generation = %u, usage = %" PRIu64 "", *generation, *usage);
+
+ // Cancel this buffer anyway.
+ Return<HStatus> cancelTransStatus = producer->cancelBuffer(slot, hFenceWrapper.getHandle());
+ if (!cancelTransStatus.isOk()) {
+ ALOGE("cancelBuffer transaction error: %s", cancelTransStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+
+ // Check requestBuffer return flag.
+ if (status != android::NO_ERROR) {
+ ALOGE("requestBuffer failed: %d", status);
+ return asC2Error(status);
+ }
+
+ mSpareSlot = slot;
+ mSpareDequeueDelayUs.reset();
+ ALOGV("Spare slot index = %d", mSpareSlot);
+ return C2_OK;
+}
+
+c2_status_t C2VdaBqBlockPool::Impl::dequeueBuffer(HGraphicBufferProducer* const producer,
+ uint32_t width, uint32_t height,
+ uint32_t pixelFormat,
+ C2AndroidMemoryUsage androidUsage,
+ int32_t& status, int32_t& slot,
+ sp<Fence>& fence) {
+ using Input = HGraphicBufferProducer::DequeueBufferInput;
+ using Output = HGraphicBufferProducer::DequeueBufferOutput;
+ bool needRealloc = false;
+ Return<void> transStatus = producer->dequeueBuffer(
+ Input{width, height, pixelFormat, androidUsage.asGrallocUsage()},
+ [&status, &slot, &needRealloc, &fence](HStatus hStatus, int32_t hSlot,
+ Output const& hOutput) {
+ slot = hSlot;
+ if (!h2b(hStatus, &status) || !h2b(hOutput.fence, &fence)) {
+ status = android::BAD_VALUE;
+ } else {
+ needRealloc = hOutput.bufferNeedsReallocation;
+ if (needRealloc) {
+ status = BUFFER_NEEDS_REALLOCATION;
+ }
+ }
+ });
+
+ // Check dequeueBuffer transaction status
+ if (!transStatus.isOk()) {
+ ALOGE("dequeueBuffer transaction error: %s", transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ // Check dequeueBuffer return flag
+ if (status != android::NO_ERROR && status != BUFFER_NEEDS_REALLOCATION) {
+ ALOGE("dequeueBuffer failed: %d", status);
+ return asC2Error(status);
+ }
+ return C2_OK;
+}
+
+void C2VdaBqBlockPool::Impl::setRenderCallback(
+ const C2BufferQueueBlockPool::OnRenderCallback& renderCallback) {
+ ALOGV("setRenderCallback");
+ std::lock_guard<std::mutex> lock(mMutex);
+ mRenderCallback = renderCallback;
+}
+
+c2_status_t C2VdaBqBlockPool::Impl::requestNewBufferSet(int32_t bufferCount) {
+ if (bufferCount <= 0) {
+ ALOGE("Invalid requested buffer count = %d", bufferCount);
+ return C2_BAD_VALUE;
+ }
+
+ if (!mAllocateBuffersLock.try_lock_for(kTimedMutexTimeoutMs)) {
+ ALOGE("Cannot acquire allocate buffers / configure producer lock over %" PRId64 " ms...",
+ static_cast<int64_t>(kTimedMutexTimeoutMs.count()));
+ return C2_BLOCKING;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mProducer) {
+ ALOGD("No HGraphicBufferProducer is configured...");
+ return C2_NO_INIT;
+ }
+
+ if (mProducerSwitched) {
+ // Some slots can be occupied by buffers transferred from the old producer. They will not
+ // used in the current producer. Free the slots of the buffers here. But we cannot find a
+ // slot is associated with the staled buffer. We free all slots whose associated buffers
+ // are not owned by client.
+ ALOGI("requestNewBufferSet: detachBuffer all slots forcedly");
+ for (int32_t slot = 0; slot < static_cast<int32_t>(NUM_BUFFER_SLOTS); ++slot) {
+ if (mSlotAllocations.find(slot) != mSlotAllocations.end()) {
+ // Skip detaching the buffer which is owned by client now.
+ continue;
+ }
+ Return<HStatus> transStatus = mProducer->detachBuffer(slot);
+ if (!transStatus.isOk()) {
+ ALOGE("detachBuffer trans error: %s", transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ int32_t status;
+ if (!h2b(static_cast<HStatus>(transStatus), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status == android::NO_INIT) {
+ // No more active buffer slot. Break the loop now.
+ break;
+ }
+ }
+ mProducerSwitched = false;
+ }
+
+ ALOGV("Requested new buffer count: %d, still dequeued buffer count: %zu", bufferCount,
+ mSlotAllocations.size());
+
+ // The remained slot indices in |mSlotAllocations| now are still dequeued (un-available).
+ // maxDequeuedBufferCount should be set to "new requested buffer count" + "still dequeued buffer
+ // count" to make sure it has enough available slots to request buffer from.
+ // Moreover, one extra buffer count is added for fetching spare buffer slot index.
+ Return<HStatus> transStatus =
+ mProducer->setMaxDequeuedBufferCount(bufferCount + mSlotAllocations.size() + 1);
+ if (!transStatus.isOk()) {
+ ALOGE("setMaxDequeuedBufferCount trans error: %s", transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ int32_t status;
+ if (!h2b(static_cast<HStatus>(transStatus), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("setMaxDequeuedBufferCount failed");
+ return asC2Error(status);
+ }
+
+ // Release all remained slot buffer references here. CCodec should either cancel or queue its
+ // owned buffers from this set before the next resolution change.
+ mSlotAllocations.clear();
+ mProducerChangeSlotMap.clear();
+ mBuffersRequested = static_cast<size_t>(bufferCount);
+ mSpareSlot = -1;
+
+ Return<HStatus> transStatus2 = mProducer->allowAllocation(true);
+ if (!transStatus2.isOk()) {
+ ALOGE("allowAllocation(true) transaction error: %s", transStatus2.description().c_str());
+ return C2_CORRUPTED;
+ }
+ if (!h2b(static_cast<HStatus>(transStatus2), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("allowAllocation(true) failed");
+ return asC2Error(status);
+ }
+ return C2_OK;
+}
+
+void C2VdaBqBlockPool::Impl::configureProducer(const sp<HGraphicBufferProducer>& producer) {
+ ALOGV("configureProducer");
+ if (producer == nullptr) {
+ ALOGE("input producer is nullptr...");
+ return;
+ }
+
+ std::unique_lock<std::timed_mutex> configureProducerLock(
+ mConfigureProducerAndAllocateBuffersMutex, std::defer_lock);
+ if (!configureProducerLock.try_lock_for(kTimedMutexTimeoutMs)) {
+ ALOGE("Cannot acquire configure producer / allocate buffers lock over %" PRId64 " ms...",
+ static_cast<int64_t>(kTimedMutexTimeoutMs.count()));
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ uint64_t producerId;
+ Return<uint64_t> transStatus = producer->getUniqueId();
+ if (!transStatus.isOk()) {
+ ALOGE("getUniqueId transaction error: %s", transStatus.description().c_str());
+ return;
+ }
+ producerId = static_cast<uint64_t>(transStatus);
+
+ if (mProducer && mProducerId != producerId) {
+ ALOGI("Producer (Surface) is going to switch... ( %" PRIu64 " -> %" PRIu64 " )",
+ mProducerId, producerId);
+ if (!switchProducer(producer.get(), producerId)) {
+ mProducerChangeSlotMap.clear();
+ return;
+ }
+ } else {
+ mSlotAllocations.clear();
+ }
+
+ // HGraphicBufferProducer could (and should) be replaced if the client has set a new generation
+ // number to producer. The old HGraphicBufferProducer will be disconnected and deprecated then.
+ mProducer = producer;
+ mProducerId = producerId;
+}
+
+bool C2VdaBqBlockPool::Impl::switchProducer(HGraphicBufferProducer* const newProducer,
+ uint64_t newProducerId) {
+ if (mAllocator->getId() == android::V4L2AllocatorId::SECURE_GRAPHIC) {
+ // TODO(johnylin): support this when we meet the use case in the future.
+ ALOGE("Switch producer for secure buffer is not supported...");
+ return false;
+ }
+
+ // Set maxDequeuedBufferCount to new producer.
+ // Just like requestNewBufferSet(), maxDequeuedBufferCount should be set to "requested buffer
+ // count" + "buffer count in client" + 1 (spare buffer) to make sure it has enough available
+ // slots to request buffer from.
+ // "Requested buffer count" could be obtained by the size of |mSlotAllocations|. However, it is
+ // not able to know "buffer count in client" in blockpool's aspect. The alternative solution is
+ // to set the worse case first, which is equal to the size of |mSlotAllocations|. And in the end
+ // of updateGraphicBlock() routine, we could get the arbitrary "buffer count in client" by
+ // counting the calls of updateGraphicBlock(willCancel=true). Then we set maxDequeuedBufferCount
+ // again to the correct value.
+ Return<HStatus> transStatus =
+ newProducer->setMaxDequeuedBufferCount(mSlotAllocations.size() * 2 + 1);
+ if (!transStatus.isOk()) {
+ ALOGE("setMaxDequeuedBufferCount trans error: %s", transStatus.description().c_str());
+ return false;
+ }
+ int32_t status;
+ if (!h2b(static_cast<HStatus>(transStatus), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("setMaxDequeuedBufferCount failed");
+ return false;
+ }
+
+ // Reset "buffer count in client". It will be accumulated in updateGraphicBlock() routine.
+ mBuffersInClient = 0;
+
+ // Set allowAllocation to new producer.
+ Return<HStatus> transStatus2 = newProducer->allowAllocation(true);
+ if (!transStatus2.isOk()) {
+ ALOGE("allowAllocation(true) transaction error: %s", transStatus2.description().c_str());
+ return false;
+ }
+ if (!h2b(static_cast<HStatus>(transStatus2), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("allowAllocation(true) failed");
+ return false;
+ }
+
+ // Fetch spare buffer slot from new producer first, this step also allows us to obtain the
+ // generation number and usage of new producer. While attaching buffers, generation number and
+ // usage must be aligned to the producer.
+ uint32_t newGeneration;
+ uint64_t newUsage;
+ c2_status_t err = fetchSpareBufferSlot(newProducer, mBufferFormat.mWidth, mBufferFormat.mHeight,
+ mBufferFormat.mPixelFormat, mBufferFormat.mUsage,
+ &newGeneration, &newUsage);
+ if (err != C2_OK) {
+ ALOGE("fetchSpareBufferSlot failed: %d", err);
+ return false;
+ }
+
+ // Attach all buffers to new producer.
+ mProducerChangeSlotMap.clear();
+ int32_t slot;
+ std::map<int32_t, std::shared_ptr<C2GraphicAllocation>> newSlotAllocations;
+ for (auto iter = mSlotAllocations.begin(); iter != mSlotAllocations.end(); ++iter) {
+ // Convert C2GraphicAllocation to GraphicBuffer.
+ uint32_t width, height, format, stride, igbp_slot, generation;
+ uint64_t usage, igbp_id;
+ android::_UnwrapNativeCodec2GrallocMetadata(iter->second->handle(), &width, &height,
+ &format, &usage, &stride, &generation, &igbp_id,
+ &igbp_slot);
+ native_handle_t* grallocHandle =
+ android::UnwrapNativeCodec2GrallocHandle(iter->second->handle());
+
+ // Update generation number and usage from newly-allocated spare buffer.
+ sp<GraphicBuffer> graphicBuffer =
+ new GraphicBuffer(grallocHandle, GraphicBuffer::CLONE_HANDLE, width, height, format,
+ 1, newUsage, stride);
+ if (graphicBuffer->initCheck() != android::NO_ERROR) {
+ ALOGE("Failed to create GraphicBuffer: %d", graphicBuffer->initCheck());
+ return false;
+ }
+ graphicBuffer->setGenerationNumber(newGeneration);
+ native_handle_delete(grallocHandle);
+
+ // Convert GraphicBuffer into HBuffer.
+ HBuffer hBuffer{};
+ uint32_t hGenerationNumber{};
+ if (!b2h(graphicBuffer, &hBuffer, &hGenerationNumber)) {
+ ALOGE("Failed to convert GraphicBuffer to HBuffer");
+ return false;
+ }
+
+ // Attach HBuffer to new producer and get the attached slot index.
+ bool converted{};
+ Return<void> transStatus = newProducer->attachBuffer(
+ hBuffer, hGenerationNumber,
+ [&converted, &status, &slot](HStatus hStatus, int32_t hSlot, bool releaseAll) {
+ converted = h2b(hStatus, &status);
+ if (!converted) {
+ status = android::BAD_VALUE;
+ }
+ slot = hSlot;
+ if (converted && releaseAll && status == android::OK) {
+ status = android::INVALID_OPERATION;
+ }
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("attachBuffer trans error: %s", transStatus.description().c_str());
+ return false;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("attachBuffer failed: %d", status);
+ return false;
+ }
+
+ // Convert back to C2GraphicAllocation wrapping new producer id, generation number, usage
+ // and slot index.
+ ALOGV("buffer wraps { producer id: %" PRIu64 ", slot: %d }", newProducerId, slot);
+ C2Handle* c2Handle = android::WrapNativeCodec2GrallocHandle(
+ graphicBuffer->handle, width, height, format, newUsage, stride, newGeneration,
+ newProducerId, slot);
+ if (!c2Handle) {
+ ALOGE("WrapNativeCodec2GrallocHandle failed");
+ return false;
+ }
+ std::shared_ptr<C2GraphicAllocation> alloc;
+ c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
+ if (err != C2_OK) {
+ ALOGE("priorGraphicAllocation failed: %d", err);
+ return false;
+ }
+
+ // Store to |newSlotAllocations| and also store old-to-new producer slot map.
+ ALOGV("Transfered buffer from old producer to new, slot prev: %d -> new %d", iter->first,
+ slot);
+ newSlotAllocations[slot] = std::move(alloc);
+ mProducerChangeSlotMap[iter->first] = slot;
+ }
+
+ // Set allowAllocation to false so producer could not allocate new buffers.
+ Return<HStatus> transStatus4 = newProducer->allowAllocation(false);
+ if (!transStatus4.isOk()) {
+ ALOGE("allowAllocation(false) transaction error: %s", transStatus4.description().c_str());
+ return false;
+ }
+ if (!h2b(static_cast<HStatus>(transStatus4), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("allowAllocation(false) failed");
+ return false;
+ }
+
+ // Try to detach all buffers from old producer.
+ for (const auto& slotAllocation : mSlotAllocations) {
+ Return<HStatus> transStatus = mProducer->detachBuffer(slotAllocation.first);
+ if (!transStatus.isOk()) {
+ ALOGE("detachBuffer trans error: %s", transStatus.description().c_str());
+ return false;
+ }
+ if (!h2b(static_cast<HStatus>(transStatus), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGW("detachBuffer slot=%d from old producer failed: %d", slotAllocation.first,
+ status);
+ }
+ }
+
+ mSlotAllocations = std::move(newSlotAllocations);
+ return true;
+}
+
+c2_status_t C2VdaBqBlockPool::Impl::updateGraphicBlock(
+ bool willCancel, uint32_t oldSlot, uint32_t* newSlot,
+ std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (mProducerChangeSlotMap.empty()) {
+ ALOGD("A new buffer set is requested right after producer change, no more update needed.");
+ return C2_CANCELED;
+ }
+
+ auto it = mProducerChangeSlotMap.find(static_cast<int32_t>(oldSlot));
+ if (it == mProducerChangeSlotMap.end()) {
+ ALOGE("Cannot find old slot = %u in map...", oldSlot);
+ return C2_NOT_FOUND;
+ }
+
+ int32_t slot = it->second;
+ *newSlot = static_cast<uint32_t>(slot);
+ mProducerChangeSlotMap.erase(it);
+
+ if (willCancel) {
+ // The old C2GraphicBlock might be owned by client. Cancel this slot.
+ Return<HStatus> transStatus = mProducer->cancelBuffer(slot, hidl_handle{});
+ if (!transStatus.isOk()) {
+ ALOGE("cancelBuffer transaction error: %s", transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ // Client might try to attach the old buffer to the current producer on client's end,
+ // although it is useless for us anymore. However it will still occupy an available slot.
+ mBuffersInClient++;
+ } else {
+ // The old C2GraphicBlock is still owned by component, replace by the new one and keep this
+ // slot dequeued.
+ auto poolData =
+ std::make_shared<C2VdaBqBlockPoolData>(mProducerId, slot, shared_from_this());
+ *block = _C2BlockFactory::CreateGraphicBlock(mSlotAllocations[slot], std::move(poolData));
+ }
+
+ if (mProducerChangeSlotMap.empty()) {
+ // The updateGraphicBlock() routine is about to finish.
+ // Set the correct maxDequeuedBufferCount to producer, which is "requested buffer count" +
+ // "buffer count in client" + 1 (spare buffer).
+ ALOGV("Requested buffer count: %zu, buffer count in client: %u", mSlotAllocations.size(),
+ mBuffersInClient);
+ Return<HStatus> transStatus = mProducer->setMaxDequeuedBufferCount(mSlotAllocations.size() +
+ mBuffersInClient + 1);
+ if (!transStatus.isOk()) {
+ ALOGE("setMaxDequeuedBufferCount trans error: %s", transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ int32_t status;
+ if (!h2b(static_cast<HStatus>(transStatus), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("setMaxDequeuedBufferCount failed: %d", status);
+ return C2_CORRUPTED;
+ }
+ mProducerSwitched = true;
+ }
+
+ return C2_OK;
+}
+
+c2_status_t C2VdaBqBlockPool::Impl::getMinBuffersForDisplay(size_t* bufferCount) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mProducer) {
+ ALOGD("No HGraphicBufferProducer is configured...");
+ return C2_NO_INIT;
+ }
+
+ int32_t status, value;
+ Return<void> transStatus = mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ [&status, &value](int32_t tStatus, int32_t tValue) {
+ status = tStatus;
+ value = tValue;
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) trans error: %s",
+ transStatus.description().c_str());
+ return C2_CORRUPTED;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGE("query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) failed: %d", status);
+ return asC2Error(status);
+ }
+ if (value <= 0) {
+ ALOGE("Illegal value of NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = %d", value);
+ return C2_BAD_VALUE;
+ }
+ *bufferCount = static_cast<size_t>(value);
+ return C2_OK;
+}
+
+void C2VdaBqBlockPool::Impl::detachBuffer(uint64_t producerId, int32_t slotId) {
+ ALOGV("detachBuffer: producer id = %" PRIu64 ", slot = %d", producerId, slotId);
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (producerId == mProducerId && mProducer) {
+ Return<HStatus> transStatus = mProducer->detachBuffer(slotId);
+ if (!transStatus.isOk()) {
+ ALOGE("detachBuffer trans error: %s", transStatus.description().c_str());
+ return;
+ }
+ int32_t status;
+ if (!h2b(static_cast<HStatus>(transStatus), &status)) {
+ status = android::BAD_VALUE;
+ }
+ if (status != android::NO_ERROR) {
+ ALOGD("detachBuffer failed: %d", status);
+ return;
+ }
+
+ auto it = mSlotAllocations.find(slotId);
+ // It may happen that the slot is not included in |mSlotAllocations|, which means it is
+ // released after resolution change.
+ if (it != mSlotAllocations.end()) {
+ mSlotAllocations.erase(it);
+ }
+ }
+}
+
+C2VdaBqBlockPool::C2VdaBqBlockPool(const std::shared_ptr<C2Allocator>& allocator,
+ const local_id_t localId)
+ : C2BufferQueueBlockPool(allocator, localId), mLocalId(localId), mImpl(new Impl(allocator)) {}
+
+c2_status_t C2VdaBqBlockPool::fetchGraphicBlock(
+ uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
+ if (mImpl) {
+ return mImpl->fetchGraphicBlock(width, height, format, usage, block);
+ }
+ return C2_NO_INIT;
+}
+
+void C2VdaBqBlockPool::setRenderCallback(
+ const C2BufferQueueBlockPool::OnRenderCallback& renderCallback) {
+ if (mImpl) {
+ mImpl->setRenderCallback(renderCallback);
+ }
+}
+
+c2_status_t C2VdaBqBlockPool::requestNewBufferSet(int32_t bufferCount) {
+ if (mImpl) {
+ return mImpl->requestNewBufferSet(bufferCount);
+ }
+ return C2_NO_INIT;
+}
+
+void C2VdaBqBlockPool::configureProducer(const sp<HGraphicBufferProducer>& producer) {
+ if (mImpl) {
+ mImpl->configureProducer(producer);
+ }
+}
+
+c2_status_t C2VdaBqBlockPool::updateGraphicBlock(
+ bool willCancel, uint32_t oldSlot, uint32_t* newSlot,
+ std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
+ if (mImpl) {
+ return mImpl->updateGraphicBlock(willCancel, oldSlot, newSlot, block);
+ }
+ return C2_NO_INIT;
+}
+
+c2_status_t C2VdaBqBlockPool::getMinBuffersForDisplay(size_t* bufferCount) {
+ if (mImpl) {
+ return mImpl->getMinBuffersForDisplay(bufferCount);
+ }
+ return C2_NO_INIT;
+}
+
+C2VdaBqBlockPoolData::C2VdaBqBlockPoolData(uint64_t producerId, int32_t slotId,
+ const std::shared_ptr<C2VdaBqBlockPool::Impl>& pool)
+ : mProducerId(producerId), mSlotId(slotId), mPool(pool) {}
+
+C2VdaBqBlockPoolData::~C2VdaBqBlockPoolData() {
+ if (mShared || !mPool) {
+ return;
+ }
+ mPool->detachBuffer(mProducerId, mSlotId);
+}
diff --git a/plugin_store/V4L2PluginStore.cpp b/plugin_store/V4L2PluginStore.cpp
new file mode 100644
index 0000000..86dd018
--- /dev/null
+++ b/plugin_store/V4L2PluginStore.cpp
@@ -0,0 +1,101 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "V4L2PluginStore"
+
+#include <inttypes.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BufferPriv.h>
+#include <log/log.h>
+
+#include <v4l2_codec2/plugin_store/C2VdaBqBlockPool.h>
+#include <v4l2_codec2/plugin_store/V4L2AllocatorId.h>
+#include <v4l2_codec2/plugin_store/VendorAllocatorLoader.h>
+
+namespace android {
+
+C2Allocator* createAllocator(C2Allocator::id_t allocatorId) {
+ ALOGV("%s(allocatorId=%d)", __func__, allocatorId);
+ static std::unique_ptr<VendorAllocatorLoader> sAllocatorLoader =
+ VendorAllocatorLoader::Create();
+
+ if (sAllocatorLoader != nullptr) {
+ ALOGD("%s(): Create C2Allocator (id=%u) from VendorAllocatorLoader", __func__, allocatorId);
+ return sAllocatorLoader->createAllocator(allocatorId);
+ }
+
+ ALOGI("%s(): Fallback to create C2AllocatorGralloc (id=%u)", __func__, allocatorId);
+ return new C2AllocatorGralloc(allocatorId, true);
+}
+
+std::shared_ptr<C2Allocator> fetchAllocator(C2Allocator::id_t allocatorId) {
+ ALOGV("%s(allocatorId=%d)", __func__, allocatorId);
+ static std::mutex sMutex;
+ static std::map<C2Allocator::id_t, std::weak_ptr<C2Allocator>> sCacheAllocators;
+
+ std::lock_guard<std::mutex> lock(sMutex);
+
+ std::shared_ptr<C2Allocator> allocator;
+ auto iter = sCacheAllocators.find(allocatorId);
+ if (iter != sCacheAllocators.end()) {
+ allocator = iter->second.lock();
+ if (allocator != nullptr) {
+ return allocator;
+ }
+ }
+
+ allocator.reset(createAllocator(allocatorId));
+ sCacheAllocators[allocatorId] = allocator;
+ return allocator;
+}
+
+C2BlockPool* createBlockPool(C2Allocator::id_t allocatorId, C2BlockPool::local_id_t poolId) {
+ ALOGV("%s(allocatorId=%d, poolId=%" PRIu64 ")", __func__, allocatorId, poolId);
+
+ std::shared_ptr<C2Allocator> allocator = fetchAllocator(allocatorId);
+ if (allocator == nullptr) {
+ ALOGE("%s(): Failed to create allocator id=%u", __func__, allocatorId);
+ return nullptr;
+ }
+
+ switch (allocatorId) {
+ case V4L2AllocatorId::V4L2_BUFFERPOOL:
+ return new C2PooledBlockPool(allocator, poolId);
+
+ case V4L2AllocatorId::V4L2_BUFFERQUEUE:
+ return new C2VdaBqBlockPool(allocator, poolId);
+
+ case V4L2AllocatorId::SECURE_LINEAR:
+ return new C2PooledBlockPool(allocator, poolId);
+
+ case V4L2AllocatorId::SECURE_GRAPHIC:
+ return new C2VdaBqBlockPool(allocator, poolId);
+
+ default:
+ ALOGE("%s(): Unknown allocator id=%u", __func__, allocatorId);
+ return nullptr;
+ }
+}
+
+} // namespace android
+
+extern "C" ::C2BlockPool* CreateBlockPool(::C2Allocator::id_t allocatorId,
+ ::C2BlockPool::local_id_t poolId) {
+ ALOGV("%s(allocatorId=%d, poolId=%" PRIu64 ")", __func__, allocatorId, poolId);
+ return ::android::createBlockPool(allocatorId, poolId);
+}
+
+extern "C" ::C2Allocator* CreateAllocator(::C2Allocator::id_t allocatorId, ::c2_status_t* status) {
+ ALOGV("%s(allocatorId=%d)", __func__, allocatorId);
+
+ ::C2Allocator* res = ::android::createAllocator(allocatorId);
+ *status = (res != nullptr) ? C2_OK : C2_BAD_INDEX;
+ return res;
+}
diff --git a/plugin_store/VendorAllocatorLoader.cpp b/plugin_store/VendorAllocatorLoader.cpp
new file mode 100644
index 0000000..438df95
--- /dev/null
+++ b/plugin_store/VendorAllocatorLoader.cpp
@@ -0,0 +1,59 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VendorAllocatorLoader"
+
+#include <v4l2_codec2/plugin_store/VendorAllocatorLoader.h>
+
+#include <dlfcn.h>
+
+#include <log/log.h>
+
+namespace android {
+namespace {
+const char* kLibPath = "libv4l2_codec2_vendor_allocator.so";
+const char* kCreateAllocatorFuncName = "CreateAllocator";
+} // namespace
+
+// static
+std::unique_ptr<VendorAllocatorLoader> VendorAllocatorLoader::Create() {
+ ALOGV("%s()", __func__);
+
+ void* libHandle = dlopen(kLibPath, RTLD_NOW | RTLD_NODELETE);
+ if (!libHandle) {
+ ALOGI("%s(): Failed to load library: %s", __func__, kLibPath);
+ return nullptr;
+ }
+
+ auto createAllocatorFunc = (CreateAllocatorFunc)dlsym(libHandle, kCreateAllocatorFuncName);
+ if (!createAllocatorFunc) {
+ ALOGE("%s(): Failed to load functions: %s", __func__, kCreateAllocatorFuncName);
+ dlclose(libHandle);
+ return nullptr;
+ }
+
+ return std::unique_ptr<VendorAllocatorLoader>(
+ new VendorAllocatorLoader(libHandle, createAllocatorFunc));
+}
+
+VendorAllocatorLoader::VendorAllocatorLoader(void* libHandle,
+ CreateAllocatorFunc createAllocatorFunc)
+ : mLibHandle(libHandle), mCreateAllocatorFunc(createAllocatorFunc) {
+ ALOGV("%s()", __func__);
+}
+
+VendorAllocatorLoader::~VendorAllocatorLoader() {
+ ALOGV("%s()", __func__);
+
+ dlclose(mLibHandle);
+}
+
+C2Allocator* VendorAllocatorLoader::createAllocator(C2Allocator::id_t allocatorId) {
+ ALOGV("%s(%d)", __func__, allocatorId);
+
+ return mCreateAllocatorFunc(allocatorId);
+}
+
+} // namespace android
diff --git a/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h
new file mode 100644
index 0000000..161e176
--- /dev/null
+++ b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h
@@ -0,0 +1,138 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_V4L2_CODEC2_PLUGIN_STORE_C2_VDA_BQ_BLOCK_POOL_H
+#define ANDROID_V4L2_CODEC2_PLUGIN_STORE_C2_VDA_BQ_BLOCK_POOL_H
+
+#include <functional>
+#include <map>
+
+#include <C2BqBufferPriv.h>
+#include <C2Buffer.h>
+#include <C2PlatformSupport.h>
+
+/**
+ * Marks the BlockPoolData in |sharedBlock| as shared. The destructor of BlockPoolData would not
+ * call detachBuffer to BufferQueue if it is shared.
+ *
+ * \param sharedBlock the C2ConstGraphicBlock which is about to pass to client.
+ */
+c2_status_t MarkBlockPoolDataAsShared(const C2ConstGraphicBlock& sharedBlock);
+
+/**
+ * The BufferQueue-backed block pool design which supports to request arbitrary count of graphic
+ * buffers from IGBP, and use this buffer set among codec component and client.
+ *
+ * The block pool should restore the mapping table between slot indices and GraphicBuffer (or
+ * C2GraphicAllocation). When component requests a new buffer, the block pool calls dequeueBuffer
+ * to IGBP to obtain a valid slot index, and returns the corresponding buffer from map.
+ */
+class C2VdaBqBlockPool : public C2BufferQueueBlockPool {
+public:
+ C2VdaBqBlockPool(const std::shared_ptr<C2Allocator>& allocator, const local_id_t localId);
+
+ ~C2VdaBqBlockPool() override = default;
+
+ /**
+ * Extracts slot index as pool ID from the graphic block.
+ *
+ * \note C2VdaBqBlockPool-specific function
+ *
+ * \param block the graphic block allocated by bufferqueue block pool.
+ * \param poolId raw pointer where slot index in bufferqueue is stored.
+ */
+ static c2_status_t getPoolIdFromGraphicBlock(const std::shared_ptr<C2GraphicBlock>& block,
+ uint32_t* poolId);
+
+ /**
+ * It's a trick here. Return C2PlatformAllocatorStore::BUFFERQUEUE instead of the ID of backing
+ * allocator for client's query. It's because in platform side this ID is recognized as
+ * BufferQueue-backed block pool which is only allowed to set surface.
+ */
+ C2Allocator::id_t getAllocatorId() const override {
+ return android::C2PlatformAllocatorStore::BUFFERQUEUE;
+ };
+
+ local_id_t getLocalId() const override { return mLocalId; };
+
+ /**
+ * Tries to dequeue a buffer from producer. If the producer is allowed allocation now, call
+ * requestBuffer of dequeued slot for allocating new buffer and storing the reference into
+ * |mSlotAllocations|.
+ *
+ * When the size of |mSlotAllocations| reaches the requested buffer count, set disallow
+ * allocation to producer. After that buffer set is started to be recycled by dequeue.
+ *
+ * \retval C2_BAD_STATE informs the caller producer is switched.
+ */
+ c2_status_t fetchGraphicBlock(uint32_t width, uint32_t height, uint32_t format,
+ C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicBlock>* block /* nonnull */) override;
+
+ void setRenderCallback(const C2BufferQueueBlockPool::OnRenderCallback& renderCallback =
+ C2BufferQueueBlockPool::OnRenderCallback()) override;
+ void configureProducer(const android::sp<HGraphicBufferProducer>& producer) override;
+
+ /**
+ * Sends the request of arbitrary number of graphic buffers allocation. If producer is given,
+ * it will set maxDequeuedBufferCount with regard to the requested buffer count and allow
+ * allocation to producer.
+ *
+ * \note C2VdaBqBlockPool-specific function
+ * \note caller should release all buffer references obtained from fetchGraphicBlock() before
+ * calling this function.
+ *
+ * \param bufferCount the number of requested buffers
+ *
+ * \retval C2_OK the operation was successful.
+ * \retval C2_NO_INIT this class is not initialized, or producer is not assigned.
+ * \retval C2_BAD_VALUE |bufferCount| is not greater than zero.
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected).
+ */
+ c2_status_t requestNewBufferSet(int32_t bufferCount);
+
+ /**
+ * Updates the buffer from producer switch.
+ *
+ * \note C2VdaBqBlockPool-specific function
+ *
+ * \param willCancel if true, the corresponding slot will be canceled to new producer. Otherwise
+ * the new graphic block will be returned as |block|.
+ * \param oldSlot the slot index from old producer the caller provided.
+ * \param newSlot the corresponding slot index of new producer is filled.
+ * \param block if |willCancel| is false, the new graphic block is stored.
+ *
+ * \retval C2_OK the operation was successful.
+ * \retval C2_NO_INIT this class is not initialized.
+ * \retval C2_NOT_FOUND cannot find |oldSlot| in the slot changing map.
+ * \retval C2_CANCELED indicates buffer format is changed and a new buffer set is allocated, no
+ * more update needed.
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected).
+ */
+ c2_status_t updateGraphicBlock(bool willCancel, uint32_t oldSlot, uint32_t* newSlot,
+ std::shared_ptr<C2GraphicBlock>* block /* nonnull */);
+
+ /**
+ * Gets minimum undequeued buffer count for display from producer.
+ *
+ * \note C2VdaBqBlockPool-specific function
+ *
+ * \param bufferCount the minimum undequeued buffer count for display is filled.
+ *
+ * \retval C2_OK the operation was successful.
+ * \retval C2_NO_INIT this class is not initialized, or producer is not assigned.
+ * \retval C2_BAD_VALUE the queried value is illegal (less than 0).
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected).
+ */
+ c2_status_t getMinBuffersForDisplay(size_t* bufferCount);
+
+private:
+ friend struct C2VdaBqBlockPoolData;
+ class Impl;
+
+ const local_id_t mLocalId;
+ std::shared_ptr<Impl> mImpl;
+};
+
+#endif // ANDROID_V4L2_CODEC2_PLUGIN_STORE_C2_VDA_BQ_BLOCK_POOL_H
diff --git a/plugin_store/include/v4l2_codec2/plugin_store/V4L2AllocatorId.h b/plugin_store/include/v4l2_codec2/plugin_store/V4L2AllocatorId.h
new file mode 100644
index 0000000..0808963
--- /dev/null
+++ b/plugin_store/include/v4l2_codec2/plugin_store/V4L2AllocatorId.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_V4L2_CODEC2_PLUGIN_STORE_V4L2_ALLOCATOR_ID_H
+#define ANDROID_V4L2_CODEC2_PLUGIN_STORE_V4L2_ALLOCATOR_ID_H
+
+#include <C2PlatformSupport.h>
+
+namespace android {
+namespace V4L2AllocatorId {
+
+// The allocator ids used for V4L2DecodeComponent.
+enum : C2AllocatorStore::id_t {
+ V4L2_BUFFERQUEUE = C2PlatformAllocatorStore::PLATFORM_END,
+ V4L2_BUFFERPOOL,
+ SECURE_LINEAR,
+ SECURE_GRAPHIC,
+};
+
+} // namespace V4L2AllocatorId
+} // namespace android
+
+#endif // ANDROID_V4L2_CODEC2_PLUGIN_STORE_V4L2_ALLOCATOR_ID_H
diff --git a/plugin_store/include/v4l2_codec2/plugin_store/VendorAllocatorLoader.h b/plugin_store/include/v4l2_codec2/plugin_store/VendorAllocatorLoader.h
new file mode 100644
index 0000000..f3f7613
--- /dev/null
+++ b/plugin_store/include/v4l2_codec2/plugin_store/VendorAllocatorLoader.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_V4L2_CODEC2_PLUGIN_STORE_VENDOR_ALLOCATOR_LOADER_H
+#define ANDROID_V4L2_CODEC2_PLUGIN_STORE_VENDOR_ALLOCATOR_LOADER_H
+
+#include <memory>
+#include <mutex>
+
+#include <C2Buffer.h>
+
+namespace android {
+
+// This class is for loading the vendor-specific C2Allocator implementations.
+// The vendor should implement the shared library "libv4l2_codec2_vendor_allocator.so"
+// and expose the function "C2Allocator* CreateAllocator(C2Allocator::id_t allocatorId);".
+class VendorAllocatorLoader {
+public:
+ using CreateAllocatorFunc = ::C2Allocator* (*)(C2Allocator::id_t /* allocatorId */);
+
+ static std::unique_ptr<VendorAllocatorLoader> Create();
+ ~VendorAllocatorLoader();
+
+ // Delegate to the vendor's shared library. |allocatorId| should be one of enum listed at
+ // V4L2AllocatorId.h.
+ C2Allocator* createAllocator(C2Allocator::id_t allocatorId);
+
+private:
+ VendorAllocatorLoader(void* libHandle, CreateAllocatorFunc createAllocatorFunc);
+
+ void* mLibHandle;
+ CreateAllocatorFunc mCreateAllocatorFunc;
+};
+
+} // namespace android
+#endif // ANDROID_V4L2_CODEC2_PLUGIN_STORE_VENDOR_ALLOCATOR_LOADER_H
diff --git a/tests/c2_e2e_test/jni/mediacodec_encoder.cpp b/tests/c2_e2e_test/jni/mediacodec_encoder.cpp
index f0ece33..e4c90a4 100644
--- a/tests/c2_e2e_test/jni/mediacodec_encoder.cpp
+++ b/tests/c2_e2e_test/jni/mediacodec_encoder.cpp
@@ -34,10 +34,12 @@
constexpr int kBufferPeriodTimeoutUs = 1000000; // 1 sec
// Helper function to get possible encoder names from |type|.
+// Note: A single test APK is built for both ARC++ and ARCVM, so both the C2 VEA encoder and the new
+// V4L2 encoder names need to be specified here.
std::vector<const char*> GetArcVideoEncoderNames(VideoCodecType type) {
switch (type) {
case VideoCodecType::H264:
- return {"c2.v4l2.avc.encoder"};
+ return {"c2.v4l2.avc.encoder", "c2.vea.avc.encoder"};
default: // unsupported type: VP8, VP9, or unknown
return {};
}