aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChih-Yu Huang <akahuang@google.com>2020-09-03 16:12:39 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-09-03 16:12:39 +0000
commit789409daebb127bc0fc4647be6a428e38fedc27d (patch)
tree15b302105bc458b9189b9ea1fa3c39e9966c7183
parentd13dccf0008e331dd60e27064cbf0342605366e0 (diff)
parentfd0e98187f9a8969e76127f1926733321fd5ef88 (diff)
downloadv4l2_codec2-789409daebb127bc0fc4647be6a428e38fedc27d.tar.gz
Make VideoFramePool and C2VdaBqBlockPool non-blocking when fetching am: 9cf0ab57d1 am: fd0e98187f
Original change: https://googleplex-android-review.googlesource.com/c/platform/external/v4l2_codec2/+/12497570 Change-Id: I6e363ac7eb122705d98fe110b1426d5816e555bc
-rw-r--r--components/VideoFramePool.cpp100
-rw-r--r--components/include/v4l2_codec2/components/VideoFramePool.h11
-rw-r--r--plugin_store/Android.bp2
-rw-r--r--plugin_store/C2VdaBqBlockPool.cpp209
-rw-r--r--plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h13
5 files changed, 149 insertions, 186 deletions
diff --git a/components/VideoFramePool.cpp b/components/VideoFramePool.cpp
index 22b622d..b6bbfab 100644
--- a/components/VideoFramePool.cpp
+++ b/components/VideoFramePool.cpp
@@ -57,6 +57,17 @@ c2_status_t VideoFramePool::requestNewBufferSet(C2BlockPool& blockPool, int32_t
}
// static
+bool VideoFramePool::setNotifyBlockAvailableCb(C2BlockPool& blockPool, ::base::OnceClosure cb) {
+ ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId());
+
+ if (blockPool.getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+ C2VdaBqBlockPool* bqPool = static_cast<C2VdaBqBlockPool*>(&blockPool);
+ return bqPool->setNotifyBlockAvailableCb(std::move(cb));
+ }
+ return false;
+}
+
+// static
std::unique_ptr<VideoFramePool> VideoFramePool::Create(
std::shared_ptr<C2BlockPool> blockPool, const size_t numBuffers, const media::Size& size,
HalPixelFormat pixelFormat, bool isSecure,
@@ -106,7 +117,6 @@ VideoFramePool::~VideoFramePool() {
ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
mClientWeakThisFactory.InvalidateWeakPtrs();
- mCancelGetFrame = true;
if (mFetchThread.IsRunning()) {
mFetchTaskRunner->PostTask(FROM_HERE,
@@ -136,48 +146,68 @@ bool VideoFramePool::getVideoFrame(GetVideoFrameCB cb) {
return true;
}
+// static
+void VideoFramePool::getVideoFrameTaskThunk(
+ scoped_refptr<::base::SequencedTaskRunner> taskRunner,
+ std::optional<::base::WeakPtr<VideoFramePool>> weakPool) {
+ ALOGV("%s()", __func__);
+ ALOG_ASSERT(weakPool);
+
+ taskRunner->PostTask(FROM_HERE,
+ ::base::BindOnce(&VideoFramePool::getVideoFrameTask, *weakPool));
+}
+
void VideoFramePool::getVideoFrameTask() {
ALOGV("%s()", __func__);
ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
- // Initial delay: 64us
- constexpr size_t kFetchRetryDelayInit = 64;
- // Max delay: 16ms (1 frame at 60fps)
- constexpr size_t kFetchRetryDelayMax = 16384;
- std::optional<FrameWithBlockId> frameWithBlockId;
- size_t numRetries = 0;
- size_t delay = kFetchRetryDelayInit;
- while (true) {
- if (mCancelGetFrame) {
- ALOGW("Request to get frame canceled after %zu retries", numRetries);
- break;
+ // Variables used to exponential backoff retry when buffer fetching times out.
+ constexpr size_t kFetchRetryDelayInit = 64; // Initial delay: 64us
+ constexpr size_t kFetchRetryDelayMax = 16384; // Max delay: 16ms (1 frame at 60fps)
+ static size_t sNumRetries = 0;
+ static size_t sDelay = kFetchRetryDelayInit;
+
+ std::shared_ptr<C2GraphicBlock> block;
+ c2_status_t err = mBlockPool->fetchGraphicBlock(mSize.width(), mSize.height(),
+ static_cast<uint32_t>(mPixelFormat),
+ mMemoryUsage, &block);
+ if (err == C2_TIMED_OUT || err == C2_BLOCKING) {
+ if (setNotifyBlockAvailableCb(*mBlockPool,
+ ::base::BindOnce(&VideoFramePool::getVideoFrameTaskThunk,
+ mFetchTaskRunner, mFetchWeakThis))) {
+ ALOGV("%s(): fetchGraphicBlock() timeout, waiting for block available.", __func__);
+ } else {
+ ALOGV("%s(): fetchGraphicBlock() timeout, waiting %zuus (%zu retry)", __func__, sDelay,
+ sNumRetries + 1);
+ mFetchTaskRunner->PostDelayedTask(
+ FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis),
+ ::base::TimeDelta::FromMicroseconds(sDelay));
+
+ sDelay = std::min(sDelay * 2, kFetchRetryDelayMax); // Exponential backoff
+ sNumRetries++;
}
- std::shared_ptr<C2GraphicBlock> block;
- c2_status_t err = mBlockPool->fetchGraphicBlock(mSize.width(), mSize.height(),
- static_cast<uint32_t>(mPixelFormat),
- mMemoryUsage, &block);
-
- if (err == C2_OK) {
- ALOG_ASSERT(block != nullptr);
- std::optional<uint32_t> bufferId = getBufferIdFromGraphicBlock(*mBlockPool, *block);
- std::unique_ptr<VideoFrame> frame = VideoFrame::Create(std::move(block));
- // Only pass the frame + id pair if both have successfully been obtained.
- // Otherwise exit the loop so a nullopt is passed to the client.
- if (bufferId && frame) {
- frameWithBlockId = std::make_pair(std::move(frame), *bufferId);
- }
- break;
- } else if (err != C2_TIMED_OUT && err != C2_BLOCKING) {
- ALOGE("Failed to fetch block, err=%d, retry %zu times", err, numRetries);
- break;
+ return;
+ }
+
+ // Reset to the default value.
+ sNumRetries = 0;
+ sDelay = kFetchRetryDelayInit;
+
+ std::optional<FrameWithBlockId> frameWithBlockId;
+ if (err == C2_OK) {
+ ALOG_ASSERT(block != nullptr);
+ std::optional<uint32_t> bufferId = getBufferIdFromGraphicBlock(*mBlockPool, *block);
+ std::unique_ptr<VideoFrame> frame = VideoFrame::Create(std::move(block));
+ // Only pass the frame + id pair if both have successfully been obtained.
+ // Otherwise exit the loop so a nullopt is passed to the client.
+ if (bufferId && frame) {
+ frameWithBlockId = std::make_pair(std::move(frame), *bufferId);
} else {
- ++numRetries;
- ALOGV("fetchGraphicBlock() timeout, waiting %zuus (%zu retry)", delay, numRetries);
- usleep(delay);
- // Exponential backoff
- delay = std::min(delay * 2, kFetchRetryDelayMax);
+ ALOGE("%s(): Failed to generate VideoFrame or get the buffer id.", __func__);
}
+ } else {
+ ALOGE("%s(): Failed to fetch block, err=%d", __func__, err);
}
mClientTaskRunner->PostTask(
diff --git a/components/include/v4l2_codec2/components/VideoFramePool.h b/components/include/v4l2_codec2/components/VideoFramePool.h
index c05c4ff..71bfe27 100644
--- a/components/include/v4l2_codec2/components/VideoFramePool.h
+++ b/components/include/v4l2_codec2/components/VideoFramePool.h
@@ -28,7 +28,7 @@ namespace android {
class VideoFramePool {
public:
using FrameWithBlockId = std::pair<std::unique_ptr<VideoFrame>, uint32_t>;
- using GetVideoFrameCB = base::OnceCallback<void(std::optional<FrameWithBlockId>)>;
+ using GetVideoFrameCB = ::base::OnceCallback<void(std::optional<FrameWithBlockId>)>;
static std::unique_ptr<VideoFramePool> Create(
std::shared_ptr<C2BlockPool> blockPool, const size_t numBuffers,
@@ -54,6 +54,8 @@ private:
bool initialize();
void destroyTask();
+ static void getVideoFrameTaskThunk(scoped_refptr<::base::SequencedTaskRunner> taskRunner,
+ std::optional<::base::WeakPtr<VideoFramePool>> weakPool);
void getVideoFrameTask();
void onVideoFrameReady(std::optional<FrameWithBlockId> frameWithBlockId);
@@ -66,6 +68,10 @@ private:
// |bufferCount| is the number of requested buffers.
static c2_status_t requestNewBufferSet(C2BlockPool& blockPool, int32_t bufferCount);
+ // Ask |blockPool| to notify when a block is available via |cb|.
+ // Return true if |blockPool| supports notifying buffer available.
+ static bool setNotifyBlockAvailableCb(C2BlockPool& blockPool, ::base::OnceClosure cb);
+
std::shared_ptr<C2BlockPool> mBlockPool;
const media::Size mSize;
const HalPixelFormat mPixelFormat;
@@ -77,9 +83,6 @@ private:
::base::Thread mFetchThread{"VideoFramePoolFetchThread"};
scoped_refptr<::base::SequencedTaskRunner> mFetchTaskRunner;
- // Set to true to unconditionally interrupt pending frame requests.
- std::atomic<bool> mCancelGetFrame = false;
-
::base::WeakPtr<VideoFramePool> mClientWeakThis;
::base::WeakPtr<VideoFramePool> mFetchWeakThis;
::base::WeakPtrFactory<VideoFramePool> mClientWeakThisFactory{this};
diff --git a/plugin_store/Android.bp b/plugin_store/Android.bp
index ed9d784..73dccaf 100644
--- a/plugin_store/Android.bp
+++ b/plugin_store/Android.bp
@@ -21,6 +21,7 @@ cc_library_shared {
],
shared_libs: [
"android.hardware.graphics.bufferqueue@2.0",
+ "libchrome",
"libcutils",
"libhardware",
"libhidlbase",
@@ -35,6 +36,7 @@ cc_library_shared {
cflags: [
"-Werror",
"-Wall",
+ "-Wno-unused-parameter", // needed for libchrome/base codes
"-Wthread-safety",
],
}
diff --git a/plugin_store/C2VdaBqBlockPool.cpp b/plugin_store/C2VdaBqBlockPool.cpp
index f0b95dd..9abc698 100644
--- a/plugin_store/C2VdaBqBlockPool.cpp
+++ b/plugin_store/C2VdaBqBlockPool.cpp
@@ -17,6 +17,7 @@
#include <C2BlockInternal.h>
#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h>
+#include <base/callback.h>
#include <log/log.h>
#include <system/window.h>
#include <types.h>
@@ -346,9 +347,6 @@ public:
explicit EventNotifier(const std::shared_ptr<Listener>& listener) : mListener(listener) {}
virtual ~EventNotifier() = default;
- // Enable or disable the notifier. The notifier would notify |mListener| when enabled.
- virtual void enable(bool enabled) = 0;
-
protected:
void notify() {
ALOGV("%s()", __func__);
@@ -367,89 +365,14 @@ public:
using EventNotifier::EventNotifier;
~BufferReleasedNotifier() override = default;
- // EventNotifier implementation
- void enable(bool enabled) override {
- ALOGV("%s(%d)", __func__, enabled);
- mEnabled = enabled;
- }
-
// HProducerListener implementation
Return<void> onBuffersReleased(uint32_t count) override {
ALOGV("%s(%u)", __func__, count);
- if (count > 0 && mEnabled.load()) {
+ if (count > 0) {
notify();
}
return {};
}
-
-private:
- std::atomic<bool> mEnabled{false};
-};
-
-// Notifies the listener with exponential backoff delay.
-class ExpDelayedNotifier : public EventNotifier {
-public:
- explicit ExpDelayedNotifier(const std::shared_ptr<Listener>& listener)
- : EventNotifier(listener) {
- mRunningThread = std::thread(&ExpDelayedNotifier::run, this);
- }
- ~ExpDelayedNotifier() override {
- ALOGV("%s()", __func__);
- {
- std::unique_lock<std::mutex> lock(mMutex);
- mDestroying = true;
- mInterruptCv.notify_one();
- }
- mRunningThread.join();
- }
-
- void enable(bool enabled) override {
- ALOGV("%s(%d)", __func__, enabled);
- std::lock_guard<std::mutex> lock(mMutex);
-
- if (mEnabled == enabled) {
- ALOGW("%s(): ExpDelayedNotifier already triggered %s.", __func__,
- enabled ? "on" : "off");
- return;
- }
- mEnabled = enabled;
- mInterruptCv.notify_one();
- }
-
-private:
- void run() {
- ALOGV("%s()", __func__);
- constexpr size_t kFetchRetryDelayInit = 1000; // Initial delay: 1ms
- constexpr size_t kFetchRetryDelayMax = 16000; // Max delay: 16ms (1 frame at 60fps)
-
- std::unique_lock<std::mutex> lock(mMutex);
- while (true) {
- mInterruptCv.wait(lock, [this]() { return mEnabled || mDestroying; });
- if (mDestroying) return;
-
- size_t delay = kFetchRetryDelayInit;
- while (mEnabled && !mDestroying) {
- mInterruptCv.wait_for(lock, delay * 1us,
- [this]() { return !mEnabled || mDestroying; });
- if (mDestroying) return;
- if (!mEnabled) break;
-
- notify();
- delay = std::min(delay * 2, kFetchRetryDelayMax);
- }
- }
- }
-
- // The background thread for exponential backoff delay.
- std::thread mRunningThread;
- // The mutex to protect other members and condition variables.
- std::mutex mMutex;
- // Used to get interrupt when the notifier is enabled or destroyed.
- std::condition_variable mInterruptCv;
- // Set to true when the notifier is enabled.
- bool mEnabled = false;
- // Set to true when the notifier is about to be destroyed.
- bool mDestroying = false;
};
/**
@@ -533,6 +456,7 @@ public:
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);
+ bool setNotifyBlockAvailableCb(::base::OnceClosure cb);
private:
friend struct C2VdaBqBlockPoolData;
@@ -550,10 +474,6 @@ private:
C2AndroidMemoryUsage mUsage = C2MemoryUsage(0);
};
- c2_status_t fetchGraphicBlockInternalLocked(
- uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
- std::shared_ptr<C2GraphicBlock>* block /* nonnull */);
-
// For C2VdaBqBlockPoolData to detach corresponding slot buffer from BufferQueue.
void detachBuffer(uint64_t producerId, int32_t slotId);
@@ -605,15 +525,13 @@ private:
// Listener for buffer release events.
sp<EventNotifier> mFetchBufferNotifier;
+
std::mutex mBufferReleaseMutex;
- // Counter for the number of invocations of onProducerBufferReleased
- uint64_t mBufferReleaseCount{0};
- // Counter for the number of invocations of configureProducer
- uint64_t mConfigCount{0};
- // Cvar which is waited upon after failed attempts to deque buffers from
- // mProducer. It is signaled when the consumer releases buffers (through
- // onProducerBufferReleased) or when the producer changes.
- std::condition_variable mBufferReleaseCv;
+ // Set to true when the buffer release event is triggered after dequeueing
+ // buffer from IGBP times out.
+ bool mBufferReleasedAfterTimedOut GUARDED_BY(mBufferReleaseMutex) = false;
+ // The callback to notify the caller the buffer is available.
+ ::base::OnceClosure mNotifyBlockAvailableCb GUARDED_BY(mBufferReleaseMutex);
};
C2VdaBqBlockPool::Impl::Impl(const std::shared_ptr<C2Allocator>& allocator)
@@ -627,48 +545,6 @@ c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlock(
ALOGV("%s()", __func__);
std::lock_guard<std::mutex> lock(mMutex);
- c2_status_t res = fetchGraphicBlockInternalLocked(width, height, format, usage, block);
- if (res != C2_TIMED_OUT) {
- ALOGV("%s() fetchGraphicBlockInternalLocked() return %d", __func__, res);
- return res;
- }
-
- mFetchBufferNotifier->enable(true);
- while (true) {
- {
- std::unique_lock<std::mutex> releaseLock(mBufferReleaseMutex);
- // Wait for either mBufferReleaseCount or mConfigCount to change.
- uint64_t curConfigCount = mConfigCount;
- uint64_t currentBufferReleaseCount = mBufferReleaseCount;
- bool success = mBufferReleaseCv.wait_for(
- releaseLock, 100ms, [currentBufferReleaseCount, curConfigCount, this]() {
- return currentBufferReleaseCount != mBufferReleaseCount ||
- curConfigCount != mConfigCount;
- });
- if (!success) {
- res = C2_TIMED_OUT;
- break;
- } else if (mConfigCount != curConfigCount) {
- res = C2_BAD_STATE;
- break;
- }
- }
-
- res = fetchGraphicBlockInternalLocked(width, height, format, usage, block);
- if (res != C2_TIMED_OUT) {
- ALOGV("%s() fetchGraphicBlockInternalLocked() return %d", __func__, res);
- break;
- }
- }
- mFetchBufferNotifier->enable(false);
- return res;
-}
-
-c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlockInternalLocked(
- uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
- std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
- ALOGV("%s()", __func__);
-
if (!mProducer) {
// Producer will not be configured in byte-buffer mode. Allocate buffers from allocator
// directly as a basic graphic block pool.
@@ -704,6 +580,10 @@ c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlockInternalLocked(
if (status == android::INVALID_OPERATION) {
status = android::TIMED_OUT;
}
+ if (status == android::TIMED_OUT) {
+ std::lock_guard<std::mutex> lock(mBufferReleaseMutex);
+ mBufferReleasedAfterTimedOut = false;
+ }
if (status != android::NO_ERROR && status != BUFFER_NEEDS_REALLOCATION) {
return asC2Error(status);
}
@@ -717,7 +597,7 @@ c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlockInternalLocked(
}
if (fenceStatus == -ETIME) { // fence wait timed out
- ALOGV("buffer fence wait timed out, wait for retry...");
+ ALOGV("%s(): buffer (slot=%d) fence wait timed out", __func__, slot);
return C2_TIMED_OUT;
}
ALOGE("buffer fence wait error: %d", fenceStatus);
@@ -805,9 +685,20 @@ c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlockInternalLocked(
void C2VdaBqBlockPool::Impl::onEventNotified() {
ALOGV("%s()", __func__);
- std::lock_guard<std::mutex> lock(mBufferReleaseMutex);
- mBufferReleaseCount++;
- mBufferReleaseCv.notify_one();
+ ::base::OnceClosure outputCb;
+ {
+ std::lock_guard<std::mutex> lock(mBufferReleaseMutex);
+
+ mBufferReleasedAfterTimedOut = true;
+ if (mNotifyBlockAvailableCb) {
+ outputCb = std::move(mNotifyBlockAvailableCb);
+ }
+ }
+
+ // Calling the callback outside the lock to avoid the deadlock.
+ if (outputCb) {
+ std::move(outputCb).Run();
+ }
}
c2_status_t C2VdaBqBlockPool::Impl::queryGenerationAndUsage(
@@ -832,7 +723,7 @@ c2_status_t C2VdaBqBlockPool::Impl::queryGenerationAndUsage(
return C2_CORRUPTED;
}
if (fenceStatus == -ETIME) { // fence wait timed out
- ALOGV("buffer fence wait timed out, wait for retry...");
+ ALOGV("%s(): buffer (slot=%d) fence wait timed out", __func__, slot);
return C2_TIMED_OUT;
}
ALOGE("buffer fence wait error: %d", fenceStatus);
@@ -966,12 +857,6 @@ void C2VdaBqBlockPool::Impl::configureProducer(const sp<HGraphicBufferProducer>&
mSlotAllocations.clear();
}
- {
- std::lock_guard<std::mutex> releaseLock(mBufferReleaseMutex);
- mConfigCount++;
- mBufferReleaseCv.notify_one();
- }
-
if (newProducer->setDequeueTimeout(0) != android::NO_ERROR) {
ALOGE("%s(): failed to setDequeueTimeout(0)", __func__);
return;
@@ -982,9 +867,6 @@ void C2VdaBqBlockPool::Impl::configureProducer(const sp<HGraphicBufferProducer>&
if (newProducer->connect(listener, 'ARC\0', false) == android::NO_ERROR) {
ALOGI("connected to ARC-specific IGBP listener.");
mFetchBufferNotifier = listener;
- } else {
- ALOGI("Fallback to exponential backoff polling.");
- mFetchBufferNotifier = new ExpDelayedNotifier(shared_from_this());
}
// HGraphicBufferProducer could (and should) be replaced if the client has set a new generation
@@ -1197,6 +1079,32 @@ void C2VdaBqBlockPool::Impl::detachBuffer(uint64_t producerId, int32_t slotId) {
}
}
+bool C2VdaBqBlockPool::Impl::setNotifyBlockAvailableCb(::base::OnceClosure cb) {
+ ALOGV("%s()", __func__);
+ if (mFetchBufferNotifier == nullptr) {
+ return false;
+ }
+
+ ::base::OnceClosure outputCb;
+ {
+ std::lock_guard<std::mutex> lock(mBufferReleaseMutex);
+
+ // If there is any buffer released after dequeueBuffer() timed out, then we could notify the
+ // caller directly.
+ if (mBufferReleasedAfterTimedOut) {
+ outputCb = std::move(cb);
+ } else {
+ mNotifyBlockAvailableCb = std::move(cb);
+ }
+ }
+
+ // Calling the callback outside the lock to avoid the deadlock.
+ if (outputCb) {
+ std::move(outputCb).Run();
+ }
+ return true;
+}
+
C2VdaBqBlockPool::C2VdaBqBlockPool(const std::shared_ptr<C2Allocator>& allocator,
const local_id_t localId)
: C2BufferQueueBlockPool(allocator, localId), mLocalId(localId), mImpl(new Impl(allocator)) {}
@@ -1246,6 +1154,13 @@ c2_status_t C2VdaBqBlockPool::getMinBuffersForDisplay(size_t* bufferCount) {
return C2_NO_INIT;
}
+bool C2VdaBqBlockPool::setNotifyBlockAvailableCb(::base::OnceClosure cb) {
+ if (mImpl) {
+ return mImpl->setNotifyBlockAvailableCb(std::move(cb));
+ }
+ return false;
+}
+
C2VdaBqBlockPoolData::C2VdaBqBlockPoolData(uint64_t producerId, int32_t slotId,
const std::shared_ptr<C2VdaBqBlockPool::Impl>& pool)
: mProducerId(producerId), mSlotId(slotId), mPool(pool) {}
diff --git a/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h
index b600716..fd524d2 100644
--- a/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h
+++ b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaBqBlockPool.h
@@ -12,6 +12,7 @@
#include <C2BqBufferPriv.h>
#include <C2Buffer.h>
#include <C2PlatformSupport.h>
+#include <base/callback_forward.h>
namespace android {
@@ -131,6 +132,18 @@ public:
*/
c2_status_t getMinBuffersForDisplay(size_t* bufferCount);
+ /**
+ * Set the callback that will be triggered when there is block available.
+ *
+ * \note C2VdaBqBlockPool-specific function
+ *
+ * \param cb the callback function that will be triggered when there is block available.
+ *
+ * Return false if we don't support to notify the caller when a buffer is available.
+ *
+ */
+ bool setNotifyBlockAvailableCb(base::OnceClosure cb);
+
private:
friend struct C2VdaBqBlockPoolData;
class Impl;