components: VideoFramePool uses dedicated thread to fetch GraphicBlock

Originally VideoFramePool shared the same thread with V4L2Decoder. It
turned out the performance is pretty bad, many frames were dropped.

In this CL, VideoFramePool creates a dedicated thread and fetch
GraphicBlock on this thread.

Bug: 146406514
Test: pass e2e test

Change-Id: I3c51cf1ce1d8e40fb52ad947d779e8c0ad2f754c
diff --git a/components/VideoFramePool.cpp b/components/VideoFramePool.cpp
index c2f68fc..acda536 100644
--- a/components/VideoFramePool.cpp
+++ b/components/VideoFramePool.cpp
@@ -12,6 +12,7 @@
 
 #include <android/hardware/graphics/common/1.0/types.h>
 #include <base/bind.h>
+#include <base/memory/ptr_util.h>
 #include <base/time/time.h>
 #include <log/log.h>
 
@@ -21,10 +22,21 @@
 
 namespace android {
 namespace {
+// The number of times and timeout used between subsequent calls when fetching graphic blocks.
 constexpr size_t kAllocateBufferMaxRetries = 32;
-constexpr size_t kFetchRetryDelayUs = 500;
+constexpr size_t kFetchRetryDelayUs = 1000;
 }  // namespace
 
+// static
+std::unique_ptr<VideoFramePool> VideoFramePool::Create(
+        std::shared_ptr<C2BlockPool> blockPool, const media::Size& size, HalPixelFormat pixelFormat,
+        bool isSecure, scoped_refptr<::base::SequencedTaskRunner> taskRunner) {
+    std::unique_ptr<VideoFramePool> pool = ::base::WrapUnique(new VideoFramePool(
+            std::move(blockPool), size, pixelFormat, isSecure, std::move(taskRunner)));
+    if (!pool->initialize()) return nullptr;
+    return pool;
+}
+
 VideoFramePool::VideoFramePool(std::shared_ptr<C2BlockPool> blockPool, const media::Size& size,
                                HalPixelFormat pixelFormat, bool isSecure,
                                scoped_refptr<::base::SequencedTaskRunner> taskRunner)
@@ -33,72 +45,110 @@
         mPixelFormat(pixelFormat),
         mMemoryUsage(isSecure ? C2MemoryUsage::READ_PROTECTED : C2MemoryUsage::CPU_READ,
                      static_cast<uint64_t>(BufferUsage::VIDEO_DECODER)),
-        mTaskRunner(std::move(taskRunner)) {
+        mClientTaskRunner(std::move(taskRunner)) {
     ALOGV("%s()", __func__);
-    ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
+    ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
     DCHECK(mBlockPool);
-    DCHECK(mTaskRunner);
+    DCHECK(mClientTaskRunner);
+}
 
-    mWeakThis = mWeakThisFactory.GetWeakPtr();
+bool VideoFramePool::initialize() {
+    if (!mFetchThread.Start()) {
+        ALOGE("Fetch thread failed to start.");
+        return false;
+    }
+    mFetchTaskRunner = mFetchThread.task_runner();
+
+    mClientWeakThis = mClientWeakThisFactory.GetWeakPtr();
+    mFetchWeakThis = mFetchWeakThisFactory.GetWeakPtr();
+
+    return true;
 }
 
 VideoFramePool::~VideoFramePool() {
     ALOGV("%s()", __func__);
-    ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
+    ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
 
-    mWeakThisFactory.InvalidateWeakPtrs();
+    mClientWeakThisFactory.InvalidateWeakPtrs();
+
+    if (mFetchThread.IsRunning()) {
+        mFetchTaskRunner->PostTask(FROM_HERE,
+                                   ::base::BindOnce(&VideoFramePool::destroyTask, mFetchWeakThis));
+        mFetchThread.Stop();
+    }
+}
+
+void VideoFramePool::destroyTask() {
+    ALOGV("%s()", __func__);
+    ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
+
+    mFetchWeakThisFactory.InvalidateWeakPtrs();
 }
 
 void VideoFramePool::getVideoFrame(GetVideoFrameCB cb) {
     ALOGV("%s()", __func__);
-    ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
+    ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
 
-    bool isRunning = !mCbQueue.empty();
-    mCbQueue.push(std::move(cb));
-    if (!isRunning) tryFetchGraphicBlock();
+    ++mNumPendingRequests;
+    mFetchTaskRunner->PostTask(FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask,
+                                                           mFetchWeakThis, std::move(cb)));
 }
 
 bool VideoFramePool::hasPendingRequests() const {
-    return !mCbQueue.empty();
+    ALOGV("%s()", __func__);
+    ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
+
+    return mNumPendingRequests > 0;
 }
 
-void VideoFramePool::tryFetchGraphicBlock() {
+void VideoFramePool::getVideoFrameTask(GetVideoFrameCB cb) {
     ALOGV("%s()", __func__);
-    ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
+    ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
 
-    if (mCbQueue.empty()) return;
+    std::unique_ptr<VideoFrame> frame = nullptr;
 
-    std::shared_ptr<C2GraphicBlock> block;
-    auto err = mBlockPool->fetchGraphicBlock(mSize.width(), mSize.height(),
-                                             static_cast<uint32_t>(mPixelFormat), mMemoryUsage,
-                                             &block);
+    size_t numRetries = 0;
+    while (numRetries < kAllocateBufferMaxRetries) {
+        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) && mNumRetries++ < kAllocateBufferMaxRetries) {
-        ALOGD("fetchGraphicBlock() timeout. retry %zu times", mNumRetries);
-        mTaskRunner->PostDelayedTask(
-                FROM_HERE, ::base::BindOnce(&VideoFramePool::tryFetchGraphicBlock, mWeakThis),
-                ::base::TimeDelta::FromMicroseconds(kFetchRetryDelayUs));
-    } else if (err != C2_OK) {
-        ALOGE("Failed to fetch block, err=%d, retry %zu times", err, mNumRetries);
-        sendVideoFrame(nullptr);
-    } else {
-        mNumRetries = 0;
-        sendVideoFrame(VideoFrame::Create(std::move(block)));
-
-        if (!mCbQueue.empty()) {
-            mTaskRunner->PostTask(
-                    FROM_HERE, ::base::BindOnce(&VideoFramePool::tryFetchGraphicBlock, mWeakThis));
+        if (err == C2_OK) {
+            frame = VideoFrame::Create(std::move(block));
+            break;
+        } else if (err != C2_TIMED_OUT && err != C2_BLOCKING) {
+            ALOGE("Failed to fetch block, err=%d, retry %zu times", err, numRetries);
+            break;
+        } else {
+            ++numRetries;
+            ALOGD("fetchGraphicBlock() timeout. retry %zu times", numRetries);
+            usleep(kFetchRetryDelayUs);
         }
     }
+    if (numRetries == kAllocateBufferMaxRetries) {
+        ALOGE("Timeout to fetch block, retry %zu times", numRetries);
+    }
+
+    mClientTaskRunner->PostTask(
+            FROM_HERE, ::base::BindOnce(&VideoFramePool::onVideoFrameReady, mClientWeakThis,
+                                        std::move(cb), std::move(frame)));
 }
 
-void VideoFramePool::sendVideoFrame(std::unique_ptr<VideoFrame> frame) {
+void VideoFramePool::onVideoFrameReady(GetVideoFrameCB cb, std::unique_ptr<VideoFrame> frame) {
     ALOGV("%s()", __func__);
-    ALOG_ASSERT(mTaskRunner->RunsTasksInCurrentSequence());
-    ALOG_ASSERT(!mCbQueue.empty());
+    ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
 
-    auto cb = std::move(mCbQueue.front());
-    mCbQueue.pop();
+    --mNumPendingRequests;
+
+    if (!frame) {
+        ALOGE("Failed to get GraphicBlock, abandoning all pending requests.");
+        mClientWeakThisFactory.InvalidateWeakPtrs();
+        mClientWeakThis = mClientWeakThisFactory.GetWeakPtr();
+
+        mNumPendingRequests = 0;
+    }
+
     std::move(cb).Run(std::move(frame));
 }