VideoFramePool: use exponential backoff for obtaining frames

The VideoFramePool needs to poll its backing block pool until a buffer
becomes available. The current strategy is to sleep and repeat a number
of times before giving up, but this carries the risk that some slower
platforms will fail to receive buffers in the alloted time and introduce
flakyness. Also the constant time between requests results in many
requests to the block pool when it is empty, a situation that typically
corresponds to the decoder having plenty of buffers at its disposal and
thus not being in a particular hurry for a new buffer.

Switch to an exponential backoff strategy for the block pool polling,
and remove the time boundary. The exponential backoff limits the number
of queries that are done to the block pool, and the absence of time
makes the whole process safer.  In case of a real error, the backing
block pool is supposed to return a proper error code that we will
propagate anyway.

Bug: 161323057
Test: arc.VideoDecodeAccel.h264_vm does not time out waiting for buffers
on Kukui.

Change-Id: Ic3b9915926680cfd263352a7922658ec9a60b5cf
diff --git a/components/VideoFramePool.cpp b/components/VideoFramePool.cpp
index 8703acf..c96fda7 100644
--- a/components/VideoFramePool.cpp
+++ b/components/VideoFramePool.cpp
@@ -24,11 +24,6 @@
 using android::hardware::graphics::common::V1_0::BufferUsage;
 
 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 = 1000;
-}  // namespace
 
 // static
 std::unique_ptr<VideoFramePool> VideoFramePool::Create(
@@ -82,6 +77,7 @@
     ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
 
     mClientWeakThisFactory.InvalidateWeakPtrs();
+    mCancelGetFrame = true;
 
     if (mFetchThread.IsRunning()) {
         mFetchTaskRunner->PostTask(FROM_HERE,
@@ -116,11 +112,20 @@
 void VideoFramePool::getVideoFrameTask(GetVideoFrameCB cb) {
     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::unique_ptr<VideoFrame> frame = nullptr;
 
     size_t numRetries = 0;
-    while (numRetries < kAllocateBufferMaxRetries) {
+    size_t delay = kFetchRetryDelayInit;
+    while (true) {
+        if (mCancelGetFrame) {
+            ALOGW("Request to get frame canceled after %zu retries", numRetries);
+            break;
+        }
+
         std::shared_ptr<C2GraphicBlock> block;
         c2_status_t err = mBlockPool->fetchGraphicBlock(mSize.width(), mSize.height(),
                                                         static_cast<uint32_t>(mPixelFormat),
@@ -135,13 +140,12 @@
             break;
         } else {
             ++numRetries;
-            ALOGD("fetchGraphicBlock() timeout. retry %zu times", numRetries);
-            usleep(kFetchRetryDelayUs);
+            ALOGD("fetchGraphicBlock() timeout, waiting %zuus (%zu retry)", delay, numRetries);
+            usleep(delay);
+            // Exponential backoff
+            delay = std::min(delay * 2, kFetchRetryDelayMax);
         }
     }
-    if (numRetries == kAllocateBufferMaxRetries) {
-        ALOGE("Timeout to fetch block, retry %zu times", numRetries);
-    }
 
     mClientTaskRunner->PostTask(
             FROM_HERE, ::base::BindOnce(&VideoFramePool::onVideoFrameReady, mClientWeakThis,
diff --git a/components/include/v4l2_codec2/components/VideoFramePool.h b/components/include/v4l2_codec2/components/VideoFramePool.h
index 7097ad9..1de525e 100644
--- a/components/include/v4l2_codec2/components/VideoFramePool.h
+++ b/components/include/v4l2_codec2/components/VideoFramePool.h
@@ -5,6 +5,7 @@
 #ifndef ANDROID_V4L2_CODEC2_COMPONENTS_VIDEO_FRAME_POOL_H
 #define ANDROID_V4L2_CODEC2_COMPONENTS_VIDEO_FRAME_POOL_H
 
+#include <atomic>
 #include <memory>
 #include <queue>
 
@@ -66,6 +67,9 @@
     ::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};