codec2: support dynamic resolution change

Bug: 112745692
Test: Install ExoPlayerDemo.apk and play "Google Glass (MP4, H264)" and Widevine Dash: "Secure SD (MP4, H264)"
Change-Id: I6d4135dd9eb8ae84383872aa5e237c1986a9a39f
diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp
index 1f30f7d..325bf6b 100644
--- a/C2VDAComponent.cpp
+++ b/C2VDAComponent.cpp
@@ -63,6 +63,7 @@
 
 const uint32_t kDpbOutputBufferExtraCount = 3;  // Use the same number as ACodec.
 const int kDequeueRetryDelayUs = 10000;  // Wait time of dequeue buffer retry in microseconds.
+const int32_t kAllocateBufferMaxRetries = 10;  // Max retry time for fetchGraphicBlock timeout.
 }  // namespace
 
 C2VDAComponent::IntfImpl::IntfImpl(C2String name, const std::shared_ptr<C2ReflectorHelper>& helper)
@@ -366,8 +367,14 @@
         return;
     }
 
-    // TODO(johnylin): when buffer is returned, we should confirm that output format is not changed
-    //                 yet. If changed, just let the buffer be released.
+    if (block->width() != static_cast<uint32_t>(mOutputFormat.mCodedSize.width()) ||
+        block->height() != static_cast<uint32_t>(mOutputFormat.mCodedSize.height())) {
+        // Output buffer is returned after we changed output resolution. Just let the buffer be
+        // released.
+        ALOGV("Discard obsolete graphic block: pool id=%u", poolId);
+        return;
+    }
+
     GraphicBlockInfo* info = getGraphicBlockByPoolId(poolId);
     if (!info) {
         reportError(C2_CORRUPTED);
@@ -405,11 +412,12 @@
     mBuffersInClient++;
 
     // Attach output buffer to the work corresponded to bitstreamId.
-    auto block = info->mGraphicBlock;
-    work->worklets.front()->output.buffers.emplace_back(C2Buffer::CreateGraphicBuffer(
-            block->share(C2Rect(mOutputFormat.mVisibleRect.width(),
-                                mOutputFormat.mVisibleRect.height()),
-                         C2Fence())));
+    C2ConstGraphicBlock constBlock = info->mGraphicBlock->share(
+            C2Rect(mOutputFormat.mVisibleRect.width(), mOutputFormat.mVisibleRect.height()),
+            C2Fence());
+    MarkBlockPoolDataAsShared(constBlock);
+    work->worklets.front()->output.buffers.emplace_back(
+            C2Buffer::CreateGraphicBuffer(std::move(constBlock)));
     info->mGraphicBlock.reset();
 
     // TODO: this does not work for timestamps as they can wrap around
@@ -652,14 +660,15 @@
     ALOGV("tryChangeOutputFormat");
     CHECK(mPendingOutputFormat);
 
-    // Change the output format only after all output buffers are returned
-    // from clients.
-    // TODO(johnylin): don't need to wait for new proposed buffer flow.
+    // At this point, all output buffers should not be owned by accelerator. The component is not
+    // able to know when a client will release all owned output buffers by now. But it is ok to
+    // leave them to client since componenet won't own those buffers anymore.
+    // TODO(johnylin): we may also set a parameter for component to keep dequeueing buffers and
+    //                 change format only after the component owns most buffers. This may prevent
+    //                 too many buffers are still on client's hand while component starts to
+    //                 allocate more buffers. However, it leads latency on output format change.
     for (const auto& info : mGraphicBlocks) {
-        if (info.mState == GraphicBlockInfo::State::OWNED_BY_CLIENT) {
-            ALOGV("wait buffer: %d for output format change", info.mBlockId);
-            return;
-        }
+        CHECK(info.mState != GraphicBlockInfo::State::OWNED_BY_ACCELERATOR);
     }
 
     CHECK_EQ(mPendingOutputFormat->mPixelFormat, HalPixelFormat::YCbCr_420_888);
@@ -749,13 +758,23 @@
         std::shared_ptr<C2GraphicBlock> block;
         C2MemoryUsage usage = {
                 mSecureMode ? C2MemoryUsage::READ_PROTECTED : C2MemoryUsage::CPU_READ, 0};
-        err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage, &block);
-        if (err != C2_OK) {
-            mGraphicBlocks.clear();
-            ALOGE("failed to allocate buffer: %d", err);
-            reportError(err);
-            return err;
+
+        int32_t retries_left = kAllocateBufferMaxRetries;
+        err = C2_NO_INIT;
+        while (err != C2_OK) {
+            err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage,
+                                               &block);
+            if (err == C2_TIMED_OUT && retries_left > 0) {
+                ALOGD("allocate buffer timeout, %d retry time(s) left...", retries_left);
+                retries_left--;
+            } else if (err != C2_OK) {
+                mGraphicBlocks.clear();
+                ALOGE("failed to allocate buffer: %d", err);
+                reportError(err);
+                return err;
+            }
         }
+
         uint32_t poolId;
         if (useBufferQueue) {
             err = C2VdaBqBlockPool::getPoolIdFromGraphicBlock(block, &poolId);