CCodec: handle delay config update

Bug: 130223947
Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small
Test: manual test with software codecs changing delay randomly
Change-Id: Ia574522a44df22f9638bc4049d7418843a76b57b
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index f5a4d94..de28820 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1724,8 +1724,11 @@
                             & C2FrameData::FLAG_DISCARD_FRAME) == 0) {
 
                 // copy buffer info to config
-                std::vector<std::unique_ptr<C2Param>> updates =
-                    std::move(work->worklets.front()->output.configUpdate);
+                std::vector<std::unique_ptr<C2Param>> updates;
+                for (const std::unique_ptr<C2Param> &param
+                        : work->worklets.front()->output.configUpdate) {
+                    updates.push_back(C2Param::Copy(*param));
+                }
                 unsigned stream = 0;
                 for (const std::shared_ptr<C2Buffer> &buf : work->worklets.front()->output.buffers) {
                     for (const std::shared_ptr<const C2Info> &info : buf->info()) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 7669421..eb20b20 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -210,22 +210,36 @@
     }
 }
 
+// Input
+
+CCodecBufferChannel::Input::Input() : extraBuffers("extra") {}
+
 // CCodecBufferChannel
 
 CCodecBufferChannel::CCodecBufferChannel(
         const std::shared_ptr<CCodecCallback> &callback)
     : mHeapSeqNum(-1),
       mCCodecCallback(callback),
-      mNumInputSlots(kSmoothnessFactor),
-      mNumOutputSlots(kSmoothnessFactor),
       mDelay(0),
       mFrameIndex(0u),
       mFirstValidFrameIndex(0u),
       mMetaMode(MODE_NONE),
       mInputMetEos(false) {
     mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + kRenderingDepth;
-    Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-    buffers->reset(new DummyInputBuffers(""));
+    {
+        Mutexed<Input>::Locked input(mInput);
+        input->buffers.reset(new DummyInputBuffers(""));
+        input->extraBuffers.flush();
+        input->inputDelay = 0u;
+        input->pipelineDelay = 0u;
+        input->numSlots = kSmoothnessFactor;
+        input->numExtraSlots = 0u;
+    }
+    {
+        Mutexed<Output>::Locked output(mOutput);
+        output->outputDelay = 0u;
+        output->numSlots = kSmoothnessFactor;
+    }
 }
 
 CCodecBufferChannel::~CCodecBufferChannel() {
@@ -255,7 +269,7 @@
     return mInputSurface->signalEndOfInputStream();
 }
 
-status_t CCodecBufferChannel::queueInputBufferInternal(const sp<MediaCodecBuffer> &buffer) {
+status_t CCodecBufferChannel::queueInputBufferInternal(sp<MediaCodecBuffer> buffer) {
     int64_t timeUs;
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
@@ -287,13 +301,31 @@
 
     uint64_t queuedFrameIndex = work->input.ordinal.frameIndex.peeku();
     std::vector<std::shared_ptr<C2Buffer>> queuedBuffers;
+    sp<Codec2Buffer> copy;
 
     if (buffer->size() > 0u) {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        Mutexed<Input>::Locked input(mInput);
         std::shared_ptr<C2Buffer> c2buffer;
-        if (!(*buffers)->releaseBuffer(buffer, &c2buffer, false)) {
+        if (!input->buffers->releaseBuffer(buffer, &c2buffer, false)) {
             return -ENOENT;
         }
+        // TODO: we want to delay copying buffers.
+        if (input->extraBuffers.numComponentBuffers() < input->numExtraSlots) {
+            copy = input->buffers->cloneAndReleaseBuffer(buffer);
+            if (copy != nullptr) {
+                (void)input->extraBuffers.assignSlot(copy);
+                if (!input->extraBuffers.releaseSlot(copy, &c2buffer, false)) {
+                    return UNKNOWN_ERROR;
+                }
+                bool released = input->buffers->releaseBuffer(buffer, nullptr, true);
+                ALOGV("[%s] queueInputBuffer: buffer copied; %sreleased",
+                      mName, released ? "" : "not ");
+                buffer.clear();
+            } else {
+                ALOGW("[%s] queueInputBuffer: failed to copy a buffer; this may cause input "
+                      "buffer starvation on component.", mName);
+            }
+        }
         work->input.buffers.push_back(c2buffer);
         queuedBuffers.push_back(c2buffer);
     } else if (eos) {
@@ -343,9 +375,15 @@
         }
     }
     if (err == C2_OK) {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        bool released = (*buffers)->releaseBuffer(buffer, nullptr, true);
-        ALOGV("[%s] queueInputBuffer: buffer %sreleased", mName, released ? "" : "not ");
+        Mutexed<Input>::Locked input(mInput);
+        bool released = false;
+        if (buffer) {
+            released = input->buffers->releaseBuffer(buffer, nullptr, true);
+        } else if (copy) {
+            released = input->extraBuffers.releaseSlot(copy, nullptr, true);
+        }
+        ALOGV("[%s] queueInputBuffer: buffer%s %sreleased",
+              mName, (buffer == nullptr) ? "(copy)" : "", released ? "" : "not ");
     }
 
     feedInputBufferIfAvailableInternal();
@@ -492,20 +530,21 @@
            mPipelineWatcher.lock()->pipelineFull()) {
         return;
     } else {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if ((*buffers)->numClientBuffers() >= mNumOutputSlots) {
+        Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers->numClientBuffers() >= output->numSlots) {
             return;
         }
     }
-    for (size_t i = 0; i < mNumInputSlots; ++i) {
+    size_t numInputSlots = mInput.lock()->numSlots;
+    for (size_t i = 0; i < numInputSlots; ++i) {
         sp<MediaCodecBuffer> inBuffer;
         size_t index;
         {
-            Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-            if ((*buffers)->numClientBuffers() >= mNumInputSlots) {
+            Mutexed<Input>::Locked input(mInput);
+            if (input->buffers->numClientBuffers() >= input->numSlots) {
                 return;
             }
-            if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
+            if (!input->buffers->requestNewBuffer(&index, &inBuffer)) {
                 ALOGV("[%s] no new buffer available", mName);
                 break;
             }
@@ -521,9 +560,9 @@
     std::shared_ptr<C2Buffer> c2Buffer;
     bool released = false;
     {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if (*buffers) {
-            released = (*buffers)->releaseBuffer(buffer, &c2Buffer);
+        Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers) {
+            released = output->buffers->releaseBuffer(buffer, &c2Buffer);
         }
     }
     // NOTE: some apps try to releaseOutputBuffer() with timestamp and/or render
@@ -685,14 +724,14 @@
     ALOGV("[%s] discardBuffer: %p", mName, buffer.get());
     bool released = false;
     {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr, true)) {
+        Mutexed<Input>::Locked input(mInput);
+        if (input->buffers && input->buffers->releaseBuffer(buffer, nullptr, true)) {
             released = true;
         }
     }
     {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr)) {
+        Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers && output->buffers->releaseBuffer(buffer, nullptr)) {
             released = true;
         }
     }
@@ -707,24 +746,24 @@
 
 void CCodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
     array->clear();
-    Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+    Mutexed<Input>::Locked input(mInput);
 
-    if (!(*buffers)->isArrayMode()) {
-        *buffers = (*buffers)->toArrayMode(mNumInputSlots);
+    if (!input->buffers->isArrayMode()) {
+        input->buffers = input->buffers->toArrayMode(input->numSlots);
     }
 
-    (*buffers)->getArray(array);
+    input->buffers->getArray(array);
 }
 
 void CCodecBufferChannel::getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
     array->clear();
-    Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+    Mutexed<Output>::Locked output(mOutput);
 
-    if (!(*buffers)->isArrayMode()) {
-        *buffers = (*buffers)->toArrayMode(mNumOutputSlots);
+    if (!output->buffers->isArrayMode()) {
+        output->buffers = output->buffers->toArrayMode(output->numSlots);
     }
 
-    (*buffers)->getArray(array);
+    output->buffers->getArray(array);
 }
 
 status_t CCodecBufferChannel::start(
@@ -773,8 +812,8 @@
     uint32_t pipelineDelayValue = pipelineDelay ? pipelineDelay.value : 0;
     uint32_t outputDelayValue = outputDelay ? outputDelay.value : 0;
 
-    mNumInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor;
-    mNumOutputSlots = outputDelayValue + kSmoothnessFactor;
+    size_t numInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor;
+    size_t numOutputSlots = outputDelayValue + kSmoothnessFactor;
     mDelay = inputDelayValue + pipelineDelayValue + outputDelayValue;
 
     // TODO: get this from input format
@@ -848,14 +887,17 @@
         }
 
         bool forceArrayMode = false;
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        Mutexed<Input>::Locked input(mInput);
+        input->numSlots = numInputSlots;
+        input->extraBuffers.flush();
+        input->numExtraSlots = 0u;
         if (graphic) {
             if (mInputSurface) {
-                buffers->reset(new DummyInputBuffers(mName));
+                input->buffers.reset(new DummyInputBuffers(mName));
             } else if (mMetaMode == MODE_ANW) {
-                buffers->reset(new GraphicMetadataInputBuffers(mName));
+                input->buffers.reset(new GraphicMetadataInputBuffers(mName));
             } else {
-                buffers->reset(new GraphicInputBuffers(mNumInputSlots, mName));
+                input->buffers.reset(new GraphicInputBuffers(numInputSlots, mName));
             }
         } else {
             if (hasCryptoOrDescrambler()) {
@@ -868,7 +910,7 @@
                 if (mDealer == nullptr) {
                     mDealer = new MemoryDealer(
                             align(capacity, MemoryDealer::getAllocationAlignment())
-                                * (mNumInputSlots + 1),
+                                * (numInputSlots + 1),
                             "EncryptedLinearInputBuffers");
                     mDecryptDestination = mDealer->allocate((size_t)capacity);
                 }
@@ -877,24 +919,24 @@
                 } else {
                     mHeapSeqNum = -1;
                 }
-                buffers->reset(new EncryptedLinearInputBuffers(
+                input->buffers.reset(new EncryptedLinearInputBuffers(
                         secure, mDealer, mCrypto, mHeapSeqNum, (size_t)capacity,
-                        mNumInputSlots, mName));
+                        numInputSlots, mName));
                 forceArrayMode = true;
             } else {
-                buffers->reset(new LinearInputBuffers(mName));
+                input->buffers.reset(new LinearInputBuffers(mName));
             }
         }
-        (*buffers)->setFormat(inputFormat);
+        input->buffers->setFormat(inputFormat);
 
         if (err == C2_OK) {
-            (*buffers)->setPool(pool);
+            input->buffers->setPool(pool);
         } else {
             // TODO: error
         }
 
         if (forceArrayMode) {
-            *buffers = (*buffers)->toArrayMode(mNumInputSlots);
+            input->buffers = input->buffers->toArrayMode(numInputSlots);
         }
     }
 
@@ -903,7 +945,7 @@
         uint32_t outputGeneration;
         {
             Mutexed<OutputSurface>::Locked output(mOutputSurface);
-            output->maxDequeueBuffers = mNumOutputSlots + reorderDepth.value + kRenderingDepth;
+            output->maxDequeueBuffers = numOutputSlots + reorderDepth.value + kRenderingDepth;
             outputSurface = output->surface ?
                     output->surface->getIGraphicBufferProducer() : nullptr;
             if (outputSurface) {
@@ -1011,18 +1053,18 @@
             outputPoolId_ = pools->outputPoolId;
         }
 
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-
+        Mutexed<Output>::Locked output(mOutput);
+        output->numSlots = numOutputSlots;
         if (graphic) {
             if (outputSurface) {
-                buffers->reset(new GraphicOutputBuffers(mName));
+                output->buffers.reset(new GraphicOutputBuffers(mName));
             } else {
-                buffers->reset(new RawGraphicOutputBuffers(mNumOutputSlots, mName));
+                output->buffers.reset(new RawGraphicOutputBuffers(numOutputSlots, mName));
             }
         } else {
-            buffers->reset(new LinearOutputBuffers(mName));
+            output->buffers.reset(new LinearOutputBuffers(mName));
         }
-        (*buffers)->setFormat(outputFormat->dup());
+        output->buffers->setFormat(outputFormat->dup());
 
 
         // Try to set output surface to created block pool if given.
@@ -1038,7 +1080,7 @@
             // WORKAROUND: if we're using early CSD workaround we convert to
             //             array mode, to appease apps assuming the output
             //             buffers to be of the same size.
-            (*buffers) = (*buffers)->toArrayMode(mNumOutputSlots);
+            output->buffers = output->buffers->toArrayMode(numOutputSlots);
 
             int32_t channelCount;
             int32_t sampleRate;
@@ -1055,7 +1097,7 @@
                 if (delay || padding) {
                     // We need write access to the buffers, and we're already in
                     // array mode.
-                    (*buffers)->initSkipCutBuffer(delay, padding, sampleRate, channelCount);
+                    output->buffers->initSkipCutBuffer(delay, padding, sampleRate, channelCount);
                 }
             }
         }
@@ -1090,14 +1132,14 @@
     if (err != C2_OK) {
         return UNKNOWN_ERROR;
     }
+    size_t numInputSlots = mInput.lock()->numSlots;
     std::vector<sp<MediaCodecBuffer>> toBeQueued;
-    // TODO: use proper buffer depth instead of this random value
-    for (size_t i = 0; i < mNumInputSlots; ++i) {
+    for (size_t i = 0; i < numInputSlots; ++i) {
         size_t index;
         sp<MediaCodecBuffer> buffer;
         {
-            Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-            if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
+            Mutexed<Input>::Locked input(mInput);
+            if (!input->buffers->requestNewBuffer(&index, &buffer)) {
                 if (i == 0) {
                     ALOGW("[%s] start: cannot allocate memory at all", mName);
                     return NO_MEMORY;
@@ -1182,12 +1224,13 @@
         }
     }
     {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        (*buffers)->flush();
+        Mutexed<Input>::Locked input(mInput);
+        input->buffers->flush();
+        input->extraBuffers.flush();
     }
     {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        (*buffers)->flush(flushedWork);
+        Mutexed<Output>::Locked output(mOutput);
+        output->buffers->flush(flushedWork);
     }
     mReorderStash.lock()->flush();
     mPipelineWatcher.lock()->flush();
@@ -1210,8 +1253,11 @@
             mPipelineWatcher.lock()->onInputBufferReleased(frameIndex, arrayIndex);
     bool newInputSlotAvailable;
     {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        newInputSlotAvailable = (*buffers)->expireComponentBuffer(buffer);
+        Mutexed<Input>::Locked input(mInput);
+        newInputSlotAvailable = input->buffers->expireComponentBuffer(buffer);
+        if (!newInputSlotAvailable) {
+            (void)input->extraBuffers.expireComponentBuffer(buffer);
+        }
     }
     if (newInputSlotAvailable) {
         feedInputBufferIfAvailable();
@@ -1269,6 +1315,7 @@
         }
     }
 
+    std::optional<uint32_t> newInputDelay, newPipelineDelay;
     while (!worklet->output.configUpdate.empty()) {
         std::unique_ptr<C2Param> param;
         worklet->output.configUpdate.back().swap(param);
@@ -1280,8 +1327,10 @@
                     mReorderStash.lock()->setDepth(reorderDepth.value);
                     ALOGV("[%s] onWorkDone: updated reorder depth to %u",
                           mName, reorderDepth.value);
+                    size_t numOutputSlots = mOutput.lock()->numSlots;
                     Mutexed<OutputSurface>::Locked output(mOutputSurface);
-                    output->maxDequeueBuffers = mNumOutputSlots + reorderDepth.value + kRenderingDepth;
+                    output->maxDequeueBuffers =
+                        numOutputSlots + reorderDepth.value + kRenderingDepth;
                     if (output->surface) {
                         output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
                     }
@@ -1301,18 +1350,86 @@
                 }
                 break;
             }
+            case C2PortActualDelayTuning::CORE_INDEX: {
+                if (param->isGlobal()) {
+                    C2ActualPipelineDelayTuning pipelineDelay;
+                    if (pipelineDelay.updateFrom(*param)) {
+                        ALOGV("[%s] onWorkDone: updating pipeline delay %u",
+                              mName, pipelineDelay.value);
+                        newPipelineDelay = pipelineDelay.value;
+                        (void)mPipelineWatcher.lock()->pipelineDelay(pipelineDelay.value);
+                    }
+                }
+                if (param->forInput()) {
+                    C2PortActualDelayTuning::input inputDelay;
+                    if (inputDelay.updateFrom(*param)) {
+                        ALOGV("[%s] onWorkDone: updating input delay %u",
+                              mName, inputDelay.value);
+                        newInputDelay = inputDelay.value;
+                        (void)mPipelineWatcher.lock()->inputDelay(inputDelay.value);
+                    }
+                }
+                if (param->forOutput()) {
+                    C2PortActualDelayTuning::output outputDelay;
+                    if (outputDelay.updateFrom(*param)) {
+                        ALOGV("[%s] onWorkDone: updating output delay %u",
+                              mName, outputDelay.value);
+                        (void)mPipelineWatcher.lock()->outputDelay(outputDelay.value);
+
+                        bool outputBuffersChanged = false;
+                        Mutexed<Output>::Locked output(mOutput);
+                        output->outputDelay = outputDelay.value;
+                        size_t numOutputSlots = outputDelay.value + kSmoothnessFactor;
+                        if (output->numSlots < numOutputSlots) {
+                            output->numSlots = numOutputSlots;
+                            if (output->buffers->isArrayMode()) {
+                                OutputBuffersArray *array =
+                                    (OutputBuffersArray *)output->buffers.get();
+                                ALOGV("[%s] onWorkDone: growing output buffer array to %zu",
+                                      mName, numOutputSlots);
+                                array->grow(numOutputSlots);
+                                outputBuffersChanged = true;
+                            }
+                        }
+                        output.unlock();
+
+                        if (outputBuffersChanged) {
+                            mCCodecCallback->onOutputBuffersChanged();
+                        }
+                    }
+                }
+                break;
+            }
             default:
                 ALOGV("[%s] onWorkDone: unrecognized config update (%08X)",
                       mName, param->index());
                 break;
         }
     }
+    if (newInputDelay || newPipelineDelay) {
+        Mutexed<Input>::Locked input(mInput);
+        size_t newNumSlots =
+            newInputDelay.value_or(input->inputDelay) +
+            newPipelineDelay.value_or(input->pipelineDelay) +
+            kSmoothnessFactor;
+        if (input->buffers->isArrayMode()) {
+            if (input->numSlots >= newNumSlots) {
+                input->numExtraSlots = 0;
+            } else {
+                input->numExtraSlots = newNumSlots - input->numSlots;
+            }
+            ALOGV("[%s] onWorkDone: updated number of extra slots to %zu (input array mode)",
+                  mName, input->numExtraSlots);
+        } else {
+            input->numSlots = newNumSlots;
+        }
+    }
 
     if (outputFormat != nullptr) {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        Mutexed<Output>::Locked output(mOutput);
         ALOGD("[%s] onWorkDone: output format changed to %s",
                 mName, outputFormat->debugString().c_str());
-        (*buffers)->setFormat(outputFormat);
+        output->buffers->setFormat(outputFormat);
 
         AString mediaType;
         if (outputFormat->findString(KEY_MIME, &mediaType)
@@ -1321,7 +1438,7 @@
             int32_t sampleRate;
             if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
                     && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
-                (*buffers)->updateSkipCutBuffer(sampleRate, channelCount);
+                output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
             }
         }
     }
@@ -1356,20 +1473,18 @@
           timestamp.peekll());
 
     if (initData != nullptr) {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if ((*buffers)->registerCsd(initData, &index, &outBuffer) == OK) {
+        Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers->registerCsd(initData, &index, &outBuffer) == OK) {
             outBuffer->meta()->setInt64("timeUs", timestamp.peek());
             outBuffer->meta()->setInt32("flags", MediaCodec::BUFFER_FLAG_CODECCONFIG);
             ALOGV("[%s] onWorkDone: csd index = %zu [%p]", mName, index, outBuffer.get());
 
-            buffers.unlock();
+            output.unlock();
             mCallback->onOutputBufferAvailable(index, outBuffer);
-            buffers.lock();
         } else {
             ALOGD("[%s] onWorkDone: unable to register csd", mName);
-            buffers.unlock();
+            output.unlock();
             mCCodecCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            buffers.lock();
             return false;
         }
     }
@@ -1421,22 +1536,22 @@
             break;
         }
 
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        status_t err = (*buffers)->registerBuffer(entry.buffer, &index, &outBuffer);
+        Mutexed<Output>::Locked output(mOutput);
+        status_t err = output->buffers->registerBuffer(entry.buffer, &index, &outBuffer);
         if (err != OK) {
             bool outputBuffersChanged = false;
             if (err != WOULD_BLOCK) {
-                if (!(*buffers)->isArrayMode()) {
-                    *buffers = (*buffers)->toArrayMode(mNumOutputSlots);
+                if (!output->buffers->isArrayMode()) {
+                    output->buffers = output->buffers->toArrayMode(output->numSlots);
                 }
-                OutputBuffersArray *array = (OutputBuffersArray *)buffers->get();
+                OutputBuffersArray *array = (OutputBuffersArray *)output->buffers.get();
                 array->realloc(entry.buffer);
                 outputBuffersChanged = true;
             }
             ALOGV("[%s] sendOutputBuffers: unable to register output buffer", mName);
             reorder->defer(entry);
 
-            buffers.unlock();
+            output.unlock();
             reorder.unlock();
 
             if (outputBuffersChanged) {
@@ -1444,7 +1559,7 @@
             }
             return;
         }
-        buffers.unlock();
+        output.unlock();
         reorder.unlock();
 
         outBuffer->meta()->setInt64("timeUs", entry.timestamp);
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 9aec82d..ae57678 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -209,7 +209,7 @@
 
     void feedInputBufferIfAvailable();
     void feedInputBufferIfAvailableInternal();
-    status_t queueInputBufferInternal(const sp<MediaCodecBuffer> &buffer);
+    status_t queueInputBufferInternal(sp<MediaCodecBuffer> buffer);
     bool handleWork(
             std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
             const C2StreamInitDataInfo::output *initData);
@@ -228,13 +228,26 @@
     QueueSync mQueueSync;
     std::vector<std::unique_ptr<C2Param>> mParamsToBeSet;
 
-    size_t mNumInputSlots;
-    size_t mNumOutputSlots;
     size_t mDelay;
 
-    Mutexed<std::unique_ptr<InputBuffers>> mInputBuffers;
+    struct Input {
+        Input();
+
+        std::unique_ptr<InputBuffers> buffers;
+        size_t numSlots;
+        FlexBuffersImpl extraBuffers;
+        size_t numExtraSlots;
+        uint32_t inputDelay;
+        uint32_t pipelineDelay;
+    };
+    Mutexed<Input> mInput;
+    struct Output {
+        std::unique_ptr<OutputBuffers> buffers;
+        size_t numSlots;
+        uint32_t outputDelay;
+    };
+    Mutexed<Output> mOutput;
     Mutexed<std::list<sp<ABuffer>>> mFlushedConfigs;
-    Mutexed<std::unique_ptr<OutputBuffers>> mOutputBuffers;
 
     std::atomic_uint64_t mFrameIndex;
     std::atomic_uint64_t mFirstValidFrameIndex;
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index fb0efce..5ebd5bd 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -98,6 +98,26 @@
     }
 }
 
+// InputBuffers
+
+sp<Codec2Buffer> InputBuffers::cloneAndReleaseBuffer(const sp<MediaCodecBuffer> &buffer) {
+    sp<Codec2Buffer> copy = createNewBuffer();
+    if (copy == nullptr) {
+        return nullptr;
+    }
+    std::shared_ptr<C2Buffer> c2buffer;
+    if (!releaseBuffer(buffer, &c2buffer, true)) {
+        return nullptr;
+    }
+    if (!copy->canCopy(c2buffer)) {
+        return nullptr;
+    }
+    if (!copy->copy(c2buffer)) {
+        return nullptr;
+    }
+    return copy;
+}
+
 // OutputBuffers
 
 void OutputBuffers::initSkipCutBuffer(
@@ -197,6 +217,8 @@
     mPool.push_front(std::move(vec));
 }
 
+// FlexBuffersImpl
+
 size_t FlexBuffersImpl::assignSlot(const sp<Codec2Buffer> &buffer) {
     for (size_t i = 0; i < mBuffers.size(); ++i) {
         if (mBuffers[i].clientBuffer == nullptr
@@ -209,8 +231,6 @@
     return mBuffers.size() - 1;
 }
 
-// FlexBuffersImpl
-
 bool FlexBuffersImpl::releaseSlot(
         const sp<MediaCodecBuffer> &buffer,
         std::shared_ptr<C2Buffer> *c2buffer,
@@ -270,6 +290,14 @@
             });
 }
 
+size_t FlexBuffersImpl::numComponentBuffers() const {
+    return std::count_if(
+            mBuffers.begin(), mBuffers.end(),
+            [](const Entry &entry) {
+                return !entry.compBuffer.expired();
+            });
+}
+
 // BuffersArrayImpl
 
 void BuffersArrayImpl::initialize(
@@ -395,6 +423,14 @@
     }
 }
 
+void BuffersArrayImpl::grow(
+        size_t newSize, std::function<sp<Codec2Buffer>()> alloc) {
+    CHECK_LT(mBuffers.size(), newSize);
+    while (mBuffers.size() < newSize) {
+        mBuffers.push_back({ alloc(), std::weak_ptr<C2Buffer>(), false });
+    }
+}
+
 size_t BuffersArrayImpl::numClientBuffers() const {
     return std::count_if(
             mBuffers.begin(), mBuffers.end(),
@@ -409,6 +445,7 @@
         const FlexBuffersImpl &impl,
         size_t minSize,
         std::function<sp<Codec2Buffer>()> allocate) {
+    mAllocate = allocate;
     mImpl.initialize(impl, minSize, allocate);
 }
 
@@ -448,18 +485,14 @@
     return mImpl.numClientBuffers();
 }
 
+sp<Codec2Buffer> InputBuffersArray::createNewBuffer() {
+    return mAllocate();
+}
+
 // LinearInputBuffers
 
 bool LinearInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
-    int32_t capacity = kLinearBufferSize;
-    (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
-    if ((size_t)capacity > kMaxLinearBufferSize) {
-        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
-        capacity = kMaxLinearBufferSize;
-    }
-    // TODO: proper max input size
-    // TODO: read usage from intf
-    sp<Codec2Buffer> newBuffer = alloc((size_t)capacity);
+    sp<Codec2Buffer> newBuffer = createNewBuffer();
     if (newBuffer == nullptr) {
         return false;
     }
@@ -486,16 +519,7 @@
     mImpl.flush();
 }
 
-std::unique_ptr<InputBuffers> LinearInputBuffers::toArrayMode(
-        size_t size) {
-    int32_t capacity = kLinearBufferSize;
-    (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
-    if ((size_t)capacity > kMaxLinearBufferSize) {
-        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
-        capacity = kMaxLinearBufferSize;
-    }
-    // TODO: proper max input size
-    // TODO: read usage from intf
+std::unique_ptr<InputBuffers> LinearInputBuffers::toArrayMode(size_t size) {
     std::unique_ptr<InputBuffersArray> array(
             new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]"));
     array->setPool(mPool);
@@ -503,7 +527,9 @@
     array->initialize(
             mImpl,
             size,
-            [this, capacity] () -> sp<Codec2Buffer> { return alloc(capacity); });
+            [pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
+                return Alloc(pool, format);
+            });
     return std::move(array);
 }
 
@@ -511,16 +537,30 @@
     return mImpl.numClientBuffers();
 }
 
-sp<Codec2Buffer> LinearInputBuffers::alloc(size_t size) {
+// static
+sp<Codec2Buffer> LinearInputBuffers::Alloc(
+        const std::shared_ptr<C2BlockPool> &pool, const sp<AMessage> &format) {
+    int32_t capacity = kLinearBufferSize;
+    (void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
+    if ((size_t)capacity > kMaxLinearBufferSize) {
+        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
+        capacity = kMaxLinearBufferSize;
+    }
+
+    // TODO: read usage from intf
     C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
     std::shared_ptr<C2LinearBlock> block;
 
-    c2_status_t err = mPool->fetchLinearBlock(size, usage, &block);
+    c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block);
     if (err != C2_OK) {
         return nullptr;
     }
 
-    return LinearBlockBuffer::Allocate(mFormat, block);
+    return LinearBlockBuffer::Allocate(format, block);
+}
+
+sp<Codec2Buffer> LinearInputBuffers::createNewBuffer() {
+    return Alloc(mPool, mFormat);
 }
 
 // EncryptedLinearInputBuffers
@@ -537,7 +577,7 @@
       mUsage({0, 0}),
       mDealer(dealer),
       mCrypto(crypto),
-      mHeapSeqNum(heapSeqNum) {
+      mMemoryVector(new std::vector<Entry>){
     if (secure) {
         mUsage = { C2MemoryUsage::READ_PROTECTED, 0 };
     } else {
@@ -550,16 +590,48 @@
                   mName, i);
             break;
         }
-        mMemoryVector.push_back({std::weak_ptr<C2LinearBlock>(), memory});
+        mMemoryVector->push_back({std::weak_ptr<C2LinearBlock>(), memory, heapSeqNum});
     }
 }
 
-sp<Codec2Buffer> EncryptedLinearInputBuffers::alloc(size_t size) {
+std::unique_ptr<InputBuffers> EncryptedLinearInputBuffers::toArrayMode(size_t size) {
+    std::unique_ptr<InputBuffersArray> array(
+            new InputBuffersArray(mComponentName.c_str(), "1D-EncryptedInput[N]"));
+    array->setPool(mPool);
+    array->setFormat(mFormat);
+    array->initialize(
+            mImpl,
+            size,
+            [pool = mPool,
+             format = mFormat,
+             usage = mUsage,
+             memoryVector = mMemoryVector] () -> sp<Codec2Buffer> {
+                return Alloc(pool, format, usage, memoryVector);
+            });
+    return std::move(array);
+}
+
+
+// static
+sp<Codec2Buffer> EncryptedLinearInputBuffers::Alloc(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const sp<AMessage> &format,
+        C2MemoryUsage usage,
+        const std::shared_ptr<std::vector<EncryptedLinearInputBuffers::Entry>> &memoryVector) {
+    int32_t capacity = kLinearBufferSize;
+    (void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
+    if ((size_t)capacity > kMaxLinearBufferSize) {
+        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
+        capacity = kMaxLinearBufferSize;
+    }
+
     sp<IMemory> memory;
     size_t slot = 0;
-    for (; slot < mMemoryVector.size(); ++slot) {
-        if (mMemoryVector[slot].block.expired()) {
-            memory = mMemoryVector[slot].memory;
+    int32_t heapSeqNum = -1;
+    for (; slot < memoryVector->size(); ++slot) {
+        if (memoryVector->at(slot).block.expired()) {
+            memory = memoryVector->at(slot).memory;
+            heapSeqNum = memoryVector->at(slot).heapSeqNum;
             break;
         }
     }
@@ -568,13 +640,18 @@
     }
 
     std::shared_ptr<C2LinearBlock> block;
-    c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block);
+    c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block);
     if (err != C2_OK || block == nullptr) {
         return nullptr;
     }
 
-    mMemoryVector[slot].block = block;
-    return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum);
+    memoryVector->at(slot).block = block;
+    return new EncryptedLinearBlockBuffer(format, block, memory, heapSeqNum);
+}
+
+sp<Codec2Buffer> EncryptedLinearInputBuffers::createNewBuffer() {
+    // TODO: android_2020
+    return nullptr;
 }
 
 // GraphicMetadataInputBuffers
@@ -587,12 +664,7 @@
 
 bool GraphicMetadataInputBuffers::requestNewBuffer(
         size_t *index, sp<MediaCodecBuffer> *buffer) {
-    std::shared_ptr<C2Allocator> alloc;
-    c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
-    if (err != C2_OK) {
-        return false;
-    }
-    sp<GraphicMetadataBuffer> newBuffer = new GraphicMetadataBuffer(mFormat, alloc);
+    sp<Codec2Buffer> newBuffer = createNewBuffer();
     if (newBuffer == nullptr) {
         return false;
     }
@@ -642,6 +714,15 @@
     return mImpl.numClientBuffers();
 }
 
+sp<Codec2Buffer> GraphicMetadataInputBuffers::createNewBuffer() {
+    std::shared_ptr<C2Allocator> alloc;
+    c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
+    if (err != C2_OK) {
+        return nullptr;
+    }
+    return new GraphicMetadataBuffer(mFormat, alloc);
+}
+
 // GraphicInputBuffers
 
 GraphicInputBuffers::GraphicInputBuffers(
@@ -652,11 +733,7 @@
               kMaxLinearBufferSize * numInputSlots)) { }
 
 bool GraphicInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
-    // TODO: proper max input size
-    // TODO: read usage from intf
-    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-    sp<GraphicBlockBuffer> newBuffer = AllocateGraphicBuffer(
-            mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
+    sp<Codec2Buffer> newBuffer = createNewBuffer();
     if (newBuffer == nullptr) {
         return false;
     }
@@ -703,12 +780,20 @@
     return mImpl.numClientBuffers();
 }
 
+sp<Codec2Buffer> GraphicInputBuffers::createNewBuffer() {
+    // TODO: read usage from intf
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    return AllocateGraphicBuffer(
+            mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
+}
+
 // OutputBuffersArray
 
 void OutputBuffersArray::initialize(
         const FlexBuffersImpl &impl,
         size_t minSize,
         std::function<sp<Codec2Buffer>()> allocate) {
+    mAlloc = allocate;
     mImpl.initialize(impl, minSize, allocate);
 }
 
@@ -781,8 +866,11 @@
     mImpl.getArray(array);
 }
 
+size_t OutputBuffersArray::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
 void OutputBuffersArray::realloc(const std::shared_ptr<C2Buffer> &c2buffer) {
-    std::function<sp<Codec2Buffer>()> alloc;
     switch (c2buffer->data().type()) {
         case C2BufferData::LINEAR: {
             uint32_t size = kLinearBufferSize;
@@ -792,7 +880,7 @@
             } else {
                 size = kMaxLinearBufferSize;
             }
-            alloc = [format = mFormat, size] {
+            mAlloc = [format = mFormat, size] {
                 return new LocalLinearBuffer(format, new ABuffer(size));
             };
             break;
@@ -808,11 +896,11 @@
             ALOGD("Unsupported type: %d", (int)c2buffer->data().type());
             return;
     }
-    mImpl.realloc(alloc);
+    mImpl.realloc(mAlloc);
 }
 
-size_t OutputBuffersArray::numClientBuffers() const {
-    return mImpl.numClientBuffers();
+void OutputBuffersArray::grow(size_t newSize) {
+    mImpl.grow(newSize, mAlloc);
 }
 
 // FlexOutputBuffers
@@ -861,10 +949,8 @@
     std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray(mComponentName.c_str()));
     array->setFormat(mFormat);
     array->transferSkipCutBuffer(mSkipCutBuffer);
-    array->initialize(
-            mImpl,
-            size,
-            [this]() { return allocateArrayBuffer(); });
+    std::function<sp<Codec2Buffer>()> alloc = getAlloc();
+    array->initialize(mImpl, size, alloc);
     return std::move(array);
 }
 
@@ -906,9 +992,11 @@
     return clientBuffer;
 }
 
-sp<Codec2Buffer> LinearOutputBuffers::allocateArrayBuffer() {
-    // TODO: proper max output size
-    return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
+std::function<sp<Codec2Buffer>()> LinearOutputBuffers::getAlloc() {
+    return [format = mFormat]{
+        // TODO: proper max output size
+        return new LocalLinearBuffer(format, new ABuffer(kLinearBufferSize));
+    };
 }
 
 // GraphicOutputBuffers
@@ -917,8 +1005,10 @@
     return new DummyContainerBuffer(mFormat, buffer);
 }
 
-sp<Codec2Buffer> GraphicOutputBuffers::allocateArrayBuffer() {
-    return new DummyContainerBuffer(mFormat);
+std::function<sp<Codec2Buffer>()> GraphicOutputBuffers::getAlloc() {
+    return [format = mFormat]{
+        return new DummyContainerBuffer(format);
+    };
 }
 
 // RawGraphicOutputBuffers
@@ -952,12 +1042,14 @@
     }
 }
 
-sp<Codec2Buffer> RawGraphicOutputBuffers::allocateArrayBuffer() {
-    return ConstGraphicBlockBuffer::AllocateEmpty(
-            mFormat,
-            [lbp = mLocalBufferPool](size_t capacity) {
-                return lbp->newBuffer(capacity);
-            });
+std::function<sp<Codec2Buffer>()> RawGraphicOutputBuffers::getAlloc() {
+    return [format = mFormat, lbp = mLocalBufferPool]{
+        return ConstGraphicBlockBuffer::AllocateEmpty(
+                format,
+                [lbp](size_t capacity) {
+                    return lbp->newBuffer(capacity);
+                });
+    };
 }
 
 }  // namespace android
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index f5d9fee..461fbd5 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -134,7 +134,18 @@
      */
     virtual std::unique_ptr<InputBuffers> toArrayMode(size_t size) = 0;
 
+    /**
+     * Release the buffer obtained from requestNewBuffer(), and create a deep
+     * copy clone of the buffer.
+     *
+     * \return  the deep copy clone of the buffer; nullptr if cloning is not
+     *          possible.
+     */
+    sp<Codec2Buffer> cloneAndReleaseBuffer(const sp<MediaCodecBuffer> &buffer);
+
 protected:
+    virtual sp<Codec2Buffer> createNewBuffer() = 0;
+
     // Pool to obtain blocks for input buffers.
     std::shared_ptr<C2BlockPool> mPool;
 
@@ -346,6 +357,12 @@
      */
     size_t numClientBuffers() const;
 
+    /**
+     * Return the number of buffers that are sent to the component but not
+     * returned back yet.
+     */
+    size_t numComponentBuffers() const;
+
 private:
     friend class BuffersArrayImpl;
 
@@ -446,6 +463,16 @@
     void realloc(std::function<sp<Codec2Buffer>()> alloc);
 
     /**
+     * Grow the array to the new size. It is a programming error to supply
+     * smaller size as the new size.
+     *
+     * \param newSize[in] new size of the array.
+     * \param alloc[in]   the alllocation function for client buffers to fill
+     *                    the new empty slots.
+     */
+    void grow(size_t newSize, std::function<sp<Codec2Buffer>()> alloc);
+
+    /**
      * Return the number of buffers that are sent to the client but not released
      * yet.
      */
@@ -506,8 +533,12 @@
 
     size_t numClientBuffers() const final;
 
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
+
 private:
     BuffersArrayImpl mImpl;
+    std::function<sp<Codec2Buffer>()> mAllocate;
 };
 
 class LinearInputBuffers : public InputBuffers {
@@ -529,18 +560,18 @@
 
     void flush() override;
 
-    std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
+    std::unique_ptr<InputBuffers> toArrayMode(size_t size) override;
 
     size_t numClientBuffers() const final;
 
-    /**
-     * Allocate a client buffer with the given size. This method may be
-     * overridden to support different kind of linear buffers (e.g. encrypted).
-     */
-    virtual sp<Codec2Buffer> alloc(size_t size);
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
+
+    FlexBuffersImpl mImpl;
 
 private:
-    FlexBuffersImpl mImpl;
+    static sp<Codec2Buffer> Alloc(
+            const std::shared_ptr<C2BlockPool> &pool, const sp<AMessage> &format);
 };
 
 class EncryptedLinearInputBuffers : public LinearInputBuffers {
@@ -556,18 +587,28 @@
 
     ~EncryptedLinearInputBuffers() override = default;
 
-    sp<Codec2Buffer> alloc(size_t size) override;
+    std::unique_ptr<InputBuffers> toArrayMode(size_t size) override;
+
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
 
 private:
-    C2MemoryUsage mUsage;
-    sp<MemoryDealer> mDealer;
-    sp<ICrypto> mCrypto;
-    int32_t mHeapSeqNum;
     struct Entry {
         std::weak_ptr<C2LinearBlock> block;
         sp<IMemory> memory;
+        int32_t heapSeqNum;
     };
-    std::vector<Entry> mMemoryVector;
+
+    static sp<Codec2Buffer> Alloc(
+            const std::shared_ptr<C2BlockPool> &pool,
+            const sp<AMessage> &format,
+            C2MemoryUsage usage,
+            const std::shared_ptr<std::vector<Entry>> &memoryVector);
+
+    C2MemoryUsage mUsage;
+    sp<MemoryDealer> mDealer;
+    sp<ICrypto> mCrypto;
+    std::shared_ptr<std::vector<Entry>> mMemoryVector;
 };
 
 class GraphicMetadataInputBuffers : public InputBuffers {
@@ -591,6 +632,9 @@
 
     size_t numClientBuffers() const final;
 
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
+
 private:
     FlexBuffersImpl mImpl;
     std::shared_ptr<C2AllocatorStore> mStore;
@@ -619,6 +663,9 @@
 
     size_t numClientBuffers() const final;
 
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
+
 private:
     FlexBuffersImpl mImpl;
     std::shared_ptr<LocalBufferPool> mLocalBufferPool;
@@ -658,6 +705,11 @@
     size_t numClientBuffers() const final {
         return 0u;
     }
+
+protected:
+    sp<Codec2Buffer> createNewBuffer() override {
+        return nullptr;
+    }
 };
 
 class OutputBuffersArray : public OutputBuffers {
@@ -704,6 +756,8 @@
 
     void getArray(Vector<sp<MediaCodecBuffer>> *array) const final;
 
+    size_t numClientBuffers() const final;
+
     /**
      * Reallocate the array, filled with buffers with the same size as given
      * buffer.
@@ -712,10 +766,17 @@
      */
     void realloc(const std::shared_ptr<C2Buffer> &c2buffer);
 
-    size_t numClientBuffers() const final;
+    /**
+     * Grow the array to the new size. It is a programming error to supply
+     * smaller size as the new size.
+     *
+     * \param newSize[in] new size of the array.
+     */
+    void grow(size_t newSize);
 
 private:
     BuffersArrayImpl mImpl;
+    std::function<sp<Codec2Buffer>()> mAlloc;
 };
 
 class FlexOutputBuffers : public OutputBuffers {
@@ -755,12 +816,15 @@
     virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
 
     /**
-     * Return an appropriate Codec2Buffer object for the type of buffers, to be
-     * used as an empty array buffer.
+     * Return a function that allocates an appropriate Codec2Buffer object for
+     * the type of buffers, to be used as an empty array buffer. The function
+     * must not refer to this pointer, since it may be used after this object
+     * destructs.
      *
-     * \return  appropriate Codec2Buffer object which can copy() from C2Buffers.
+     * \return  a function that allocates appropriate Codec2Buffer object,
+     *          which can copy() from C2Buffers.
      */
-    virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
+    virtual std::function<sp<Codec2Buffer>()> getAlloc() = 0;
 
 private:
     FlexBuffersImpl mImpl;
@@ -776,7 +840,7 @@
 
     sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
 
-    sp<Codec2Buffer> allocateArrayBuffer() override;
+    std::function<sp<Codec2Buffer>()> getAlloc() override;
 };
 
 class GraphicOutputBuffers : public FlexOutputBuffers {
@@ -786,7 +850,7 @@
 
     sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
 
-    sp<Codec2Buffer> allocateArrayBuffer() override;
+    std::function<sp<Codec2Buffer>()> getAlloc() override;
 };
 
 class RawGraphicOutputBuffers : public FlexOutputBuffers {
@@ -797,7 +861,7 @@
 
     sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
 
-    sp<Codec2Buffer> allocateArrayBuffer() override;
+    std::function<sp<Codec2Buffer>()> getAlloc() override;
 
 private:
     std::shared_ptr<LocalBufferPool> mLocalBufferPool;
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index dd618aa..36dcab9 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -226,10 +226,10 @@
             const std::shared_ptr<C2GraphicBlock> &block,
             std::function<sp<ABuffer>(size_t)> alloc);
 
-    std::shared_ptr<C2Buffer> asC2Buffer() override;
-
     virtual ~GraphicBlockBuffer() = default;
 
+    std::shared_ptr<C2Buffer> asC2Buffer() override;
+
 private:
     GraphicBlockBuffer(
             const sp<AMessage> &format,
@@ -260,11 +260,10 @@
      */
     GraphicMetadataBuffer(
             const sp<AMessage> &format, const std::shared_ptr<C2Allocator> &alloc);
+    virtual ~GraphicMetadataBuffer() = default;
 
     std::shared_ptr<C2Buffer> asC2Buffer() override;
 
-    virtual ~GraphicMetadataBuffer() = default;
-
 private:
     GraphicMetadataBuffer() = delete;
 
@@ -307,12 +306,12 @@
             const sp<AMessage> &format,
             std::function<sp<ABuffer>(size_t)> alloc);
 
+    virtual ~ConstGraphicBlockBuffer() = default;
+
     std::shared_ptr<C2Buffer> asC2Buffer() override;
     bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
     bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
 
-    virtual ~ConstGraphicBlockBuffer() = default;
-
 private:
     ConstGraphicBlockBuffer(
             const sp<AMessage> &format,