Revert "RESTRICT AUTOMERGE CCodec: support multiple output per one input buffer"

This reverts commit 32ec3d75239b84797995a1e5838a331088810ce3.
(cherry picked from commit b3a53aa902112de75ea59a986bb1c53ced3cebb8)

Change-Id: I0ce23db8772734e421e96b3a132c065dc500e36e
diff --git a/media/codecs/aac/C2SoftAacEnc.cpp b/media/codecs/aac/C2SoftAacEnc.cpp
index ea72aad..3380686 100644
--- a/media/codecs/aac/C2SoftAacEnc.cpp
+++ b/media/codecs/aac/C2SoftAacEnc.cpp
@@ -154,8 +154,7 @@
       mInputTimeSet(false),
       mInputSize(0),
       mInputTimeUs(-1ll),
-      mSignalledError(false),
-      mOutIndex(0u) {
+      mSignalledError(false) {
 }
 
 C2SoftAacEnc::~C2SoftAacEnc() {
@@ -350,6 +349,7 @@
         mInputTimeUs = work->input.ordinal.timestamp;
         mInputTimeSet = true;
     }
+    uint64_t timestamp = mInputTimeUs.peeku();
 
     size_t numFrames = (capacity + mInputSize + (eos ? mNumBytesPerInputFrame - 1 : 0))
             / mNumBytesPerInputFrame;
@@ -357,11 +357,22 @@
           capacity, mInputSize, numFrames, mNumBytesPerInputFrame);
 
     std::shared_ptr<C2LinearBlock> block;
-    std::shared_ptr<C2Buffer> buffer;
     std::unique_ptr<C2WriteView> wView;
     uint8_t *outPtr = temp;
     size_t outAvailable = 0u;
-    uint64_t inputIndex = work->input.ordinal.frameIndex.peeku();
+
+    if (numFrames) {
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        // TODO: error handling, proper usage, etc.
+        c2_status_t err = pool->fetchLinearBlock(mOutBufferSize * numFrames, usage, &block);
+        if (err != C2_OK) {
+            ALOGE("err = %d", err);
+        }
+
+        wView.reset(new C2WriteView(block->map().get()));
+        outPtr = wView->data();
+        outAvailable = wView->size();
+    }
 
     AACENC_InArgs inargs;
     AACENC_OutArgs outargs;
@@ -393,58 +404,15 @@
     outBufDesc.bufSizes          = outBufferSize;
     outBufDesc.bufElSizes        = outBufferElSize;
 
+    // Encode the mInputFrame, which is treated as a modulo buffer
     AACENC_ERROR encoderErr = AACENC_OK;
-
-    class FillWork {
-    public:
-        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
-                 const std::shared_ptr<C2Buffer> &buffer)
-            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {
-        }
-        ~FillWork() = default;
-
-        void operator()(const std::unique_ptr<C2Work> &work) {
-            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
-            work->worklets.front()->output.buffers.clear();
-            work->worklets.front()->output.ordinal = mOrdinal;
-            work->workletsProcessed = 1u;
-            work->result = C2_OK;
-            if (mBuffer) {
-                work->worklets.front()->output.buffers.push_back(mBuffer);
-            }
-            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
-                  mOrdinal.timestamp.peekll(),
-                  mOrdinal.frameIndex.peekll(),
-                  mBuffer ? "" : "o");
-        }
-
-    private:
-        const uint32_t mFlags;
-        const C2WorkOrdinalStruct mOrdinal;
-        const std::shared_ptr<C2Buffer> mBuffer;
-    };
-
-    C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+    size_t nOutputBytes = 0;
 
     while (encoderErr == AACENC_OK && inargs.numInSamples > 0) {
-        if (numFrames && !block) {
-            C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-            // TODO: error handling, proper usage, etc.
-            c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
-            if (err != C2_OK) {
-                ALOGE("err = %d", err);
-            }
-
-            wView.reset(new C2WriteView(block->map().get()));
-            outPtr = wView->data();
-            outAvailable = wView->size();
-            --numFrames;
-        }
-
         memset(&outargs, 0, sizeof(outargs));
 
         outBuffer[0] = outPtr;
-        outBufferSize[0] = outAvailable;
+        outBufferSize[0] = outAvailable - nOutputBytes;
 
         encoderErr = aacEncEncode(mAACEncoder,
                                   &inBufDesc,
@@ -453,32 +421,17 @@
                                   &outargs);
 
         if (encoderErr == AACENC_OK) {
-            if (buffer) {
-                outOrdinal.frameIndex = mOutIndex++;
-                outOrdinal.timestamp = mInputTimeUs;
-                cloneAndSend(
-                        inputIndex,
-                        work,
-                        FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
-                buffer.reset();
-            }
-
             if (outargs.numOutBytes > 0) {
                 mInputSize = 0;
                 int consumed = (capacity / sizeof(int16_t)) - inargs.numInSamples
                         + outargs.numInSamples;
                 mInputTimeUs = work->input.ordinal.timestamp
                         + (consumed * 1000000ll / channelCount / sampleRate);
-                buffer = createLinearBuffer(block, 0, outargs.numOutBytes);
-#if defined(LOG_NDEBUG) && !LOG_NDEBUG
-                hexdump(outPtr, std::min(outargs.numOutBytes, 256));
-#endif
-                outPtr = temp;
-                outAvailable = 0;
-                block.reset();
             } else {
                 mInputSize += outargs.numInSamples * sizeof(int16_t);
             }
+            outPtr += outargs.numOutBytes;
+            nOutputBytes += outargs.numOutBytes;
 
             if (outargs.numInSamples > 0) {
                 inBuffer[0] = (int16_t *)inBuffer[0] + outargs.numInSamples;
@@ -486,29 +439,15 @@
                 inargs.numInSamples -= outargs.numInSamples;
             }
         }
-        ALOGV("encoderErr = %d mInputSize = %zu inargs.numInSamples = %d, mInputTimeUs = %lld",
-              encoderErr, mInputSize, inargs.numInSamples, mInputTimeUs.peekll());
+        ALOGV("encoderErr = %d nOutputBytes = %zu; mInputSize = %zu inargs.numInSamples = %d",
+              encoderErr, nOutputBytes, mInputSize, inargs.numInSamples);
     }
 
     if (eos && inBufferSize[0] > 0) {
-        if (numFrames && !block) {
-            C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-            // TODO: error handling, proper usage, etc.
-            c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
-            if (err != C2_OK) {
-                ALOGE("err = %d", err);
-            }
-
-            wView.reset(new C2WriteView(block->map().get()));
-            outPtr = wView->data();
-            outAvailable = wView->size();
-            --numFrames;
-        }
-
         memset(&outargs, 0, sizeof(outargs));
 
         outBuffer[0] = outPtr;
-        outBufferSize[0] = outAvailable;
+        outBufferSize[0] = outAvailable - nOutputBytes;
 
         // Flush
         inargs.numInSamples = -1;
@@ -518,12 +457,27 @@
                            &outBufDesc,
                            &inargs,
                            &outargs);
+
+        nOutputBytes += outargs.numOutBytes;
     }
 
-    outOrdinal.frameIndex = mOutIndex++;
-    outOrdinal.timestamp = mInputTimeUs;
-    FillWork((C2FrameData::flags_t)(eos ? C2FrameData::FLAG_END_OF_STREAM : 0),
-             outOrdinal, buffer)(work);
+    work->worklets.front()->output.flags =
+        (C2FrameData::flags_t)(eos ? C2FrameData::FLAG_END_OF_STREAM : 0);
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->worklets.front()->output.ordinal.timestamp = timestamp;
+    work->workletsProcessed = 1u;
+    if (nOutputBytes) {
+        work->worklets.front()->output.buffers.push_back(
+                createLinearBuffer(block, 0, nOutputBytes));
+    }
+
+#if 0
+    ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)",
+          nOutputBytes, mInputTimeUs.peekll(), outHeader->nFlags);
+
+    hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
+#endif
 }
 
 c2_status_t C2SoftAacEnc::drain(
diff --git a/media/codecs/aac/C2SoftAacEnc.h b/media/codecs/aac/C2SoftAacEnc.h
index 779365b..e6e7cf0 100644
--- a/media/codecs/aac/C2SoftAacEnc.h
+++ b/media/codecs/aac/C2SoftAacEnc.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_C2_SOFT_AAC_ENC_H_
 #define ANDROID_C2_SOFT_AAC_ENC_H_
 
-#include <atomic>
-
 #include <SimpleC2Component.h>
 
 #include "aacenc_lib.h"
@@ -62,7 +60,6 @@
     c2_cntr64_t mInputTimeUs;
 
     bool mSignalledError;
-    std::atomic_uint64_t mOutIndex;
 
     status_t initEncoder();
 
diff --git a/media/codecs/base/SimpleC2Component.cpp b/media/codecs/base/SimpleC2Component.cpp
index 54f094c..d6a62a7 100644
--- a/media/codecs/base/SimpleC2Component.cpp
+++ b/media/codecs/base/SimpleC2Component.cpp
@@ -132,56 +132,6 @@
     }
 }
 
-class SimpleC2Component::BlockingBlockPool : public C2BlockPool {
-public:
-    BlockingBlockPool(const std::shared_ptr<C2BlockPool>& base): mBase{base} {}
-
-    virtual local_id_t getLocalId() const override {
-        return mBase->getLocalId();
-    }
-
-    virtual C2Allocator::id_t getAllocatorId() const override {
-        return mBase->getAllocatorId();
-    }
-
-    virtual c2_status_t fetchLinearBlock(
-            uint32_t capacity,
-            C2MemoryUsage usage,
-            std::shared_ptr<C2LinearBlock>* block) {
-        c2_status_t status;
-        do {
-            status = mBase->fetchLinearBlock(capacity, usage, block);
-        } while (status == C2_TIMED_OUT);
-        return status;
-    }
-
-    virtual c2_status_t fetchCircularBlock(
-            uint32_t capacity,
-            C2MemoryUsage usage,
-            std::shared_ptr<C2CircularBlock>* block) {
-        c2_status_t status;
-        do {
-            status = mBase->fetchCircularBlock(capacity, usage, block);
-        } while (status == C2_TIMED_OUT);
-        return status;
-    }
-
-    virtual c2_status_t fetchGraphicBlock(
-            uint32_t width, uint32_t height, uint32_t format,
-            C2MemoryUsage usage,
-            std::shared_ptr<C2GraphicBlock>* block) {
-        c2_status_t status;
-        do {
-            status = mBase->fetchGraphicBlock(width, height, format, usage,
-                                              block);
-        } while (status == C2_TIMED_OUT);
-        return status;
-    }
-
-private:
-    std::shared_ptr<C2BlockPool> mBase;
-};
-
 ////////////////////////////////////////////////////////////////////////////////
 
 namespace {
@@ -411,38 +361,14 @@
     }
     if (work) {
         fillWork(work);
-        std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
+        Mutexed<ExecState>::Locked state(mExecState);
+        std::shared_ptr<C2Component::Listener> listener = state->mListener;
+        state.unlock();
         listener->onWorkDone_nb(shared_from_this(), vec(work));
         ALOGV("returning pending work");
     }
 }
 
-void SimpleC2Component::cloneAndSend(
-        uint64_t frameIndex,
-        const std::unique_ptr<C2Work> &currentWork,
-        std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
-    std::unique_ptr<C2Work> work(new C2Work);
-    if (currentWork->input.ordinal.frameIndex == frameIndex) {
-        work->input.flags = currentWork->input.flags;
-        work->input.ordinal = currentWork->input.ordinal;
-    } else {
-        Mutexed<PendingWork>::Locked pending(mPendingWork);
-        if (pending->count(frameIndex) == 0) {
-            ALOGW("unknown frame index: %" PRIu64, frameIndex);
-            return;
-        }
-        work->input.flags = pending->at(frameIndex)->input.flags;
-        work->input.ordinal = pending->at(frameIndex)->input.ordinal;
-    }
-    work->worklets.emplace_back(new C2Worklet);
-    if (work) {
-        fillWork(work);
-        std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
-        listener->onWorkDone_nb(shared_from_this(), vec(work));
-        ALOGV("cloned and sending work");
-    }
-}
-
 bool SimpleC2Component::processQueue() {
     std::unique_ptr<C2Work> work;
     uint64_t generation;
@@ -496,16 +422,12 @@
                 }
             }
 
-            std::shared_ptr<C2BlockPool> blockPool;
-            err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
+            err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
             ALOGD("Using output block pool with poolID %llu => got %llu - %d",
                     (unsigned long long)poolId,
                     (unsigned long long)(
-                            blockPool ? blockPool->getLocalId() : 111000111),
+                            mOutputBlockPool ? mOutputBlockPool->getLocalId() : 111000111),
                     err);
-            if (err == C2_OK) {
-                mOutputBlockPool = std::make_shared<BlockingBlockPool>(blockPool);
-            }
             return err;
         }();
         if (err != C2_OK) {
diff --git a/media/codecs/base/include/SimpleC2Component.h b/media/codecs/base/include/SimpleC2Component.h
index 43029a9..e745dc5 100644
--- a/media/codecs/base/include/SimpleC2Component.h
+++ b/media/codecs/base/include/SimpleC2Component.h
@@ -121,24 +121,6 @@
      */
     void finish(uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork);
 
-    /**
-     * Clone pending or current work and send the work back to client.
-     *
-     * This method will retrieve and clone the pending or current work according
-     * to |frameIndex| and feed the work into |fillWork| function. |fillWork|
-     * must be "non-blocking". Once |fillWork| returns the filled work will be
-     * returned to the client.
-     *
-     * \param[in]   frameIndex    the index of the work
-     * \param[in]   currentWork   the current work under processing
-     * \param[in]   fillWork      the function to fill the retrieved work.
-     */
-    void cloneAndSend(
-            uint64_t frameIndex,
-            const std::unique_ptr<C2Work> &currentWork,
-            std::function<void(const std::unique_ptr<C2Work> &)> fillWork);
-
-
     std::shared_ptr<C2Buffer> createLinearBuffer(
             const std::shared_ptr<C2LinearBlock> &block);
 
@@ -234,8 +216,7 @@
     typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
     Mutexed<PendingWork> mPendingWork;
 
-    class BlockingBlockPool;
-    std::shared_ptr<BlockingBlockPool> mOutputBlockPool;
+    std::shared_ptr<C2BlockPool> mOutputBlockPool;
 
     SimpleC2Component() = delete;
 };
diff --git a/media/sfplugin/CCodec.cpp b/media/sfplugin/CCodec.cpp
index 1e74658..20c3de3 100644
--- a/media/sfplugin/CCodec.cpp
+++ b/media/sfplugin/CCodec.cpp
@@ -533,10 +533,6 @@
         mCodec->onWorkQueued(eos);
     }
 
-    void onOutputBuffersChanged() override {
-        mCodec->mCallback->onOutputBuffersChanged();
-    }
-
 private:
     CCodec *mCodec;
 };
@@ -1329,10 +1325,6 @@
 
     std::list<std::unique_ptr<C2Work>> flushedWork;
     c2_status_t err = comp->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
-    {
-        Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
-        flushedWork.splice(flushedWork.end(), *queue);
-    }
     if (err != C2_OK) {
         // TODO: convert err into status_t
         mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
@@ -1580,10 +1572,7 @@
                 (new AMessage(kWhatWorkDone, this))->post();
             }
 
-            if (work->worklets.empty()
-                    || !(work->worklets.front()->output.flags & C2FrameData::FLAG_INCOMPLETE)) {
-                subQueuedWorkCount(1);
-            }
+            subQueuedWorkCount(1);
             // handle configuration changes in work done
             Mutexed<Config>::Locked config(mConfig);
             bool changed = false;
@@ -1708,7 +1697,7 @@
         deadline->set(std::chrono::steady_clock::now() + 3s, "eos");
     }
     // TODO: query and use input/pipeline/output delay combined
-    if (count >= 4) {
+    if (count >= 8) {
         CCodecWatchdog::getInstance()->watch(this);
         Mutexed<NamedTimePoint>::Locked deadline(mQueueDeadline);
         deadline->set(std::chrono::steady_clock::now() + 3s, "queue");
diff --git a/media/sfplugin/CCodecBufferChannel.cpp b/media/sfplugin/CCodecBufferChannel.cpp
index 69de800..2cdea6e 100644
--- a/media/sfplugin/CCodecBufferChannel.cpp
+++ b/media/sfplugin/CCodecBufferChannel.cpp
@@ -171,7 +171,7 @@
      * index and MediaCodecBuffer object. Returns false if registration
      * fails.
      */
-    virtual status_t registerBuffer(
+    virtual bool registerBuffer(
             const std::shared_ptr<C2Buffer> &buffer,
             size_t *index,
             sp<MediaCodecBuffer> *clientBuffer) = 0;
@@ -180,7 +180,7 @@
      * Register codec specific data as a buffer to be consistent with
      * MediaCodec behavior.
      */
-    virtual status_t registerCsd(
+    virtual bool registerCsd(
             const C2StreamCsdInfo::output * /* csd */,
             size_t * /* index */,
             sp<MediaCodecBuffer> * /* clientBuffer */) = 0;
@@ -274,7 +274,7 @@
 namespace {
 
 // TODO: get this info from component
-const static size_t kMinInputBufferArraySize = 4;
+const static size_t kMinInputBufferArraySize = 8;
 const static size_t kMaxPipelineCapacity = 18;
 const static size_t kChannelOutputDelay = 0;
 const static size_t kMinOutputBufferArraySize = kMaxPipelineCapacity +
@@ -571,33 +571,25 @@
      * \param match[in]     a function to test whether the buffer matches the
      *                      criteria or not.
      * \return OK           if successful,
-     *         WOULD_BLOCK  if slots are being used,
-     *         NO_MEMORY    if no slot matches the criteria, even though it's
-     *                      available
+     *         NO_MEMORY    if there's no available slot meets the criteria.
      */
     status_t grabBuffer(
             size_t *index,
             sp<Codec2Buffer> *buffer,
             std::function<bool(const sp<Codec2Buffer> &)> match =
                 [](const sp<Codec2Buffer> &) { return true; }) {
-        // allBuffersDontMatch remains true if all buffers are available but
-        // match() returns false for every buffer.
-        bool allBuffersDontMatch = true;
         for (size_t i = 0; i < mBuffers.size(); ++i) {
-            if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()) {
-                if (match(mBuffers[i].clientBuffer)) {
-                    mBuffers[i].ownedByClient = true;
-                    *buffer = mBuffers[i].clientBuffer;
-                    (*buffer)->meta()->clear();
-                    (*buffer)->setRange(0, (*buffer)->capacity());
-                    *index = i;
-                    return OK;
-                }
-            } else {
-                allBuffersDontMatch = false;
+            if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()
+                    && match(mBuffers[i].clientBuffer)) {
+                mBuffers[i].ownedByClient = true;
+                *buffer = mBuffers[i].clientBuffer;
+                (*buffer)->meta()->clear();
+                (*buffer)->setRange(0, (*buffer)->capacity());
+                *index = i;
+                return OK;
             }
         }
-        return allBuffersDontMatch ? NO_MEMORY : WOULD_BLOCK;
+        return NO_MEMORY;
     }
 
     /**
@@ -689,14 +681,6 @@
         }
     }
 
-    void realloc(std::function<sp<Codec2Buffer>()> alloc) {
-        size_t size = mBuffers.size();
-        mBuffers.clear();
-        for (size_t i = 0; i < size; ++i) {
-            mBuffers.push_back({ alloc(), std::weak_ptr<C2Buffer>(), false });
-        }
-    }
-
 private:
     std::string mImplName; ///< name for debugging
     const char *mName; ///< C-string version of name
@@ -1089,7 +1073,7 @@
         return nullptr;
     }
 
-    status_t registerBuffer(
+    bool registerBuffer(
             const std::shared_ptr<C2Buffer> &buffer,
             size_t *index,
             sp<MediaCodecBuffer> *clientBuffer) final {
@@ -1100,25 +1084,22 @@
                 [buffer](const sp<Codec2Buffer> &clientBuffer) {
                     return clientBuffer->canCopy(buffer);
                 });
-        if (err == WOULD_BLOCK) {
-            ALOGV("[%s] buffers temporarily not available", mName);
-            return err;
-        } else if (err != OK) {
+        if (err != OK) {
             ALOGD("[%s] grabBuffer failed: %d", mName, err);
-            return err;
+            return false;
         }
         c2Buffer->setFormat(mFormat);
         if (!c2Buffer->copy(buffer)) {
             ALOGD("[%s] copy buffer failed", mName);
-            return WOULD_BLOCK;
+            return false;
         }
         submit(c2Buffer);
         *clientBuffer = c2Buffer;
         ALOGV("[%s] grabbed buffer %zu", mName, *index);
-        return OK;
+        return true;
     }
 
-    status_t registerCsd(
+    bool registerCsd(
             const C2StreamCsdInfo::output *csd,
             size_t *index,
             sp<MediaCodecBuffer> *clientBuffer) final {
@@ -1131,13 +1112,13 @@
                             && clientBuffer->capacity() >= csd->flexCount();
                 });
         if (err != OK) {
-            return err;
+            return false;
         }
         memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
         c2Buffer->setRange(0, csd->flexCount());
         c2Buffer->setFormat(mFormat);
         *clientBuffer = c2Buffer;
-        return OK;
+        return true;
     }
 
     bool releaseBuffer(
@@ -1157,36 +1138,6 @@
         mImpl.getArray(array);
     }
 
-    void realloc(const std::shared_ptr<C2Buffer> &c2buffer) {
-        std::function<sp<Codec2Buffer>()> alloc;
-        switch (c2buffer->data().type()) {
-            case C2BufferData::LINEAR: {
-                uint32_t size = kLinearBufferSize;
-                const C2ConstLinearBlock &block = c2buffer->data().linearBlocks().front();
-                if (block.size() < kMaxLinearBufferSize / 2) {
-                    size = block.size() * 2;
-                } else {
-                    size = kMaxLinearBufferSize;
-                }
-                alloc = [format = mFormat, size] {
-                    return new LocalLinearBuffer(format, new ABuffer(size));
-                };
-                break;
-            }
-
-            // TODO: add support
-            case C2BufferData::GRAPHIC:         FALLTHROUGH_INTENDED;
-
-            case C2BufferData::INVALID:         FALLTHROUGH_INTENDED;
-            case C2BufferData::LINEAR_CHUNKS:   FALLTHROUGH_INTENDED;
-            case C2BufferData::GRAPHIC_CHUNKS:  FALLTHROUGH_INTENDED;
-            default:
-                ALOGD("Unsupported type: %d", (int)c2buffer->data().type());
-                return;
-        }
-        mImpl.realloc(alloc);
-    }
-
 private:
     BuffersArrayImpl mImpl;
 };
@@ -1197,7 +1148,7 @@
         : OutputBuffers(componentName, name),
           mImpl(mName) { }
 
-    status_t registerBuffer(
+    bool registerBuffer(
             const std::shared_ptr<C2Buffer> &buffer,
             size_t *index,
             sp<MediaCodecBuffer> *clientBuffer) override {
@@ -1206,10 +1157,10 @@
         *index = mImpl.assignSlot(newBuffer);
         *clientBuffer = newBuffer;
         ALOGV("[%s] registered buffer %zu", mName, *index);
-        return OK;
+        return true;
     }
 
-    status_t registerCsd(
+    bool registerCsd(
             const C2StreamCsdInfo::output *csd,
             size_t *index,
             sp<MediaCodecBuffer> *clientBuffer) final {
@@ -1217,7 +1168,7 @@
                 mFormat, ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
         *index = mImpl.assignSlot(newBuffer);
         *clientBuffer = newBuffer;
-        return OK;
+        return true;
     }
 
     bool releaseBuffer(
@@ -1429,57 +1380,65 @@
 // CCodecBufferChannel::PipelineCapacity
 
 CCodecBufferChannel::PipelineCapacity::PipelineCapacity()
-      : input(0), component(0),
+      : input(0), component(0), output(0),
         mName("<UNKNOWN COMPONENT>") {
 }
 
 void CCodecBufferChannel::PipelineCapacity::initialize(
         int newInput,
         int newComponent,
+        int newOutput,
         const char* newName,
         const char* callerTag) {
     input.store(newInput, std::memory_order_relaxed);
     component.store(newComponent, std::memory_order_relaxed);
+    output.store(newOutput, std::memory_order_relaxed);
     mName = newName;
     ALOGV("[%s] %s -- PipelineCapacity::initialize(): "
           "pipeline availability initialized ==> "
-          "input = %d, component = %d",
+          "input = %d, component = %d, output = %d",
             mName, callerTag ? callerTag : "*",
-            newInput, newComponent);
+            newInput, newComponent, newOutput);
 }
 
 bool CCodecBufferChannel::PipelineCapacity::allocate(const char* callerTag) {
     int prevInput = input.fetch_sub(1, std::memory_order_relaxed);
     int prevComponent = component.fetch_sub(1, std::memory_order_relaxed);
-    if (prevInput > 0 && prevComponent > 0) {
+    int prevOutput = output.fetch_sub(1, std::memory_order_relaxed);
+    if (prevInput > 0 && prevComponent > 0 && prevOutput > 0) {
         ALOGV("[%s] %s -- PipelineCapacity::allocate() returns true: "
               "pipeline availability -1 all ==> "
-              "input = %d, component = %d",
+              "input = %d, component = %d, output = %d",
                 mName, callerTag ? callerTag : "*",
                 prevInput - 1,
-                prevComponent - 1);
+                prevComponent - 1,
+                prevOutput - 1);
         return true;
     }
     input.fetch_add(1, std::memory_order_relaxed);
     component.fetch_add(1, std::memory_order_relaxed);
+    output.fetch_add(1, std::memory_order_relaxed);
     ALOGV("[%s] %s -- PipelineCapacity::allocate() returns false: "
           "pipeline availability unchanged ==> "
-          "input = %d, component = %d",
+          "input = %d, component = %d, output = %d",
             mName, callerTag ? callerTag : "*",
             prevInput,
-            prevComponent);
+            prevComponent,
+            prevOutput);
     return false;
 }
 
 void CCodecBufferChannel::PipelineCapacity::free(const char* callerTag) {
     int prevInput = input.fetch_add(1, std::memory_order_relaxed);
     int prevComponent = component.fetch_add(1, std::memory_order_relaxed);
+    int prevOutput = output.fetch_add(1, std::memory_order_relaxed);
     ALOGV("[%s] %s -- PipelineCapacity::free(): "
           "pipeline availability +1 all ==> "
-          "input = %d, component = %d",
+          "input = %d, component = %d, output = %d",
             mName, callerTag ? callerTag : "*",
             prevInput + 1,
-            prevComponent + 1);
+            prevComponent + 1,
+            prevOutput + 1);
 }
 
 int CCodecBufferChannel::PipelineCapacity::freeInputSlots(
@@ -1489,12 +1448,13 @@
                                     std::memory_order_relaxed);
     ALOGV("[%s] %s -- PipelineCapacity::freeInputSlots(%zu): "
           "pipeline availability +%zu input ==> "
-          "input = %d, component = %d",
+          "input = %d, component = %d, output = %d",
             mName, callerTag ? callerTag : "*",
             numDiscardedInputBuffers,
             numDiscardedInputBuffers,
             prevInput + static_cast<int>(numDiscardedInputBuffers),
-            component.load(std::memory_order_relaxed));
+            component.load(std::memory_order_relaxed),
+            output.load(std::memory_order_relaxed));
     return prevInput + static_cast<int>(numDiscardedInputBuffers);
 }
 
@@ -1503,84 +1463,25 @@
     int prevComponent = component.fetch_add(1, std::memory_order_relaxed);
     ALOGV("[%s] %s -- PipelineCapacity::freeComponentSlot(): "
           "pipeline availability +1 component ==> "
-          "input = %d, component = %d",
+          "input = %d, component = %d, output = %d",
             mName, callerTag ? callerTag : "*",
             input.load(std::memory_order_relaxed),
-            prevComponent + 1);
+            prevComponent + 1,
+            output.load(std::memory_order_relaxed));
     return prevComponent + 1;
 }
 
-// CCodecBufferChannel::ReorderStash
-
-CCodecBufferChannel::ReorderStash::ReorderStash() {
-    clear();
-}
-
-void CCodecBufferChannel::ReorderStash::clear() {
-    mPending.clear();
-    mStash.clear();
-    mDepth = 0;
-    mKey = C2Config::ORDINAL;
-}
-
-void CCodecBufferChannel::ReorderStash::setDepth(uint32_t depth) {
-    mPending.splice(mPending.end(), mStash);
-    mDepth = depth;
-}
-void CCodecBufferChannel::ReorderStash::setKey(C2Config::ordinal_key_t key) {
-    mPending.splice(mPending.end(), mStash);
-    mKey = key;
-}
-
-bool CCodecBufferChannel::ReorderStash::pop(Entry *entry) {
-    if (mPending.empty()) {
-        return false;
-    }
-    entry->buffer     = mPending.front().buffer;
-    entry->timestamp  = mPending.front().timestamp;
-    entry->flags      = mPending.front().flags;
-    entry->ordinal    = mPending.front().ordinal;
-    mPending.pop_front();
-    return true;
-}
-
-void CCodecBufferChannel::ReorderStash::emplace(
-        const std::shared_ptr<C2Buffer> &buffer,
-        int64_t timestamp,
-        int32_t flags,
-        const C2WorkOrdinalStruct &ordinal) {
-    for (auto it = mStash.begin(); it != mStash.end(); ++it) {
-        if (less(ordinal, it->ordinal)) {
-            mStash.emplace(it, buffer, timestamp, flags, ordinal);
-            return;
-        }
-    }
-    mStash.emplace_back(buffer, timestamp, flags, ordinal);
-    while (!mStash.empty() && mStash.size() > mDepth) {
-        mPending.push_back(mStash.front());
-        mStash.pop_front();
-    }
-}
-
-void CCodecBufferChannel::ReorderStash::defer(
-        const CCodecBufferChannel::ReorderStash::Entry &entry) {
-    mPending.push_front(entry);
-}
-
-bool CCodecBufferChannel::ReorderStash::hasPending() const {
-    return !mPending.empty();
-}
-
-bool CCodecBufferChannel::ReorderStash::less(
-        const C2WorkOrdinalStruct &o1, const C2WorkOrdinalStruct &o2) {
-    switch (mKey) {
-        case C2Config::ORDINAL:   return o1.frameIndex < o2.frameIndex;
-        case C2Config::TIMESTAMP: return o1.timestamp < o2.timestamp;
-        case C2Config::CUSTOM:    return o1.customOrdinal < o2.customOrdinal;
-        default:
-            ALOGD("Unrecognized key; default to timestamp");
-            return o1.frameIndex < o2.frameIndex;
-    }
+int CCodecBufferChannel::PipelineCapacity::freeOutputSlot(
+        const char* callerTag) {
+    int prevOutput = output.fetch_add(1, std::memory_order_relaxed);
+    ALOGV("[%s] %s -- PipelineCapacity::freeOutputSlot(): "
+          "pipeline availability +1 output ==> "
+          "input = %d, component = %d, output = %d",
+            mName, callerTag ? callerTag : "*",
+            input.load(std::memory_order_relaxed),
+            component.load(std::memory_order_relaxed),
+            prevOutput + 1);
+    return prevOutput + 1;
 }
 
 // CCodecBufferChannel
@@ -1819,7 +1720,6 @@
 
 void CCodecBufferChannel::feedInputBufferIfAvailableInternal() {
     while ((!mInputMetEos || mPendingEosTimestamp != INT64_MIN) &&
-           !mReorderStash.lock()->hasPending() &&
            mAvailablePipelineCapacity.allocate("feedInputBufferIfAvailable")) {
         int64_t pendingEosTimestamp = mPendingEosTimestamp.exchange(INT64_MIN);
         if (pendingEosTimestamp != INT64_MIN) {
@@ -1860,27 +1760,16 @@
 
 status_t CCodecBufferChannel::renderOutputBuffer(
         const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
-    ALOGV("[%s] renderOutputBuffer: %p", mName, buffer.get());
+    mAvailablePipelineCapacity.freeOutputSlot("renderOutputBuffer");
+    feedInputBufferIfAvailable();
     std::shared_ptr<C2Buffer> c2Buffer;
-    bool released = false;
     {
         Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
         if (*buffers) {
-            released = (*buffers)->releaseBuffer(buffer, &c2Buffer);
+            (*buffers)->releaseBuffer(buffer, &c2Buffer);
         }
     }
-    // NOTE: some apps try to releaseOutputBuffer() with timestamp and/or render
-    //       set to true.
-    sendOutputBuffers();
-    // input buffer feeding may have been gated by pending output buffers
-    feedInputBufferIfAvailable();
     if (!c2Buffer) {
-        if (released) {
-            ALOGD("[%s] The app is calling releaseOutputBuffer() with "
-                  "timestamp or render=true with non-video buffers. Apps should "
-                  "call releaseOutputBuffer() with render=false for those.",
-                  mName);
-        }
         return INVALID_OPERATION;
     }
 
@@ -2025,12 +1914,11 @@
         if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr)) {
             buffers.unlock();
             released = true;
+            mAvailablePipelineCapacity.freeOutputSlot("discardBuffer");
         }
     }
-    if (released) {
-        sendOutputBuffers();
-        feedInputBufferIfAvailable();
-    } else {
+    feedInputBufferIfAvailable();
+    if (!released) {
         ALOGD("[%s] MediaCodec discarded an unknown buffer", mName);
     }
     return OK;
@@ -2062,34 +1950,38 @@
         const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
     C2StreamBufferTypeSetting::input iStreamFormat(0u);
     C2StreamBufferTypeSetting::output oStreamFormat(0u);
-    C2PortReorderBufferDepthTuning::output reorderDepth;
-    C2PortReorderKeySetting::output reorderKey;
     c2_status_t err = mComponent->query(
-            {
-                &iStreamFormat,
-                &oStreamFormat,
-                &reorderDepth,
-                &reorderKey,
-            },
+            { &iStreamFormat, &oStreamFormat },
             {},
             C2_DONT_BLOCK,
             nullptr);
-    if (err == C2_BAD_INDEX) {
-        if (!iStreamFormat || !oStreamFormat) {
-            return UNKNOWN_ERROR;
-        }
-        Mutexed<ReorderStash>::Locked reorder(mReorderStash);
-        reorder->clear();
-        if (reorderDepth) {
-            reorder->setDepth(reorderDepth.value);
-        }
-        if (reorderKey) {
-            reorder->setKey(reorderKey.value);
-        }
-    } else if (err != C2_OK) {
+    if (err != C2_OK) {
         return UNKNOWN_ERROR;
     }
 
+    // Query delays
+    C2PortRequestedDelayTuning::input inputDelay;
+    C2PortRequestedDelayTuning::output outputDelay;
+    C2RequestedPipelineDelayTuning pipelineDelay;
+#if 0
+    err = mComponent->query(
+            { &inputDelay, &pipelineDelay, &outputDelay },
+            {},
+            C2_DONT_BLOCK,
+            nullptr);
+    mAvailablePipelineCapacity.initialize(
+            inputDelay,
+            inputDelay + pipelineDelay,
+            inputDelay + pipelineDelay + outputDelay,
+            mName);
+#else
+    mAvailablePipelineCapacity.initialize(
+            kMinInputBufferArraySize,
+            kMaxPipelineCapacity,
+            kMinOutputBufferArraySize,
+            mName);
+#endif
+
     // TODO: get this from input format
     bool secure = mComponent->getName().find(".secure") != std::string::npos;
 
@@ -2313,11 +2205,11 @@
                     outputGeneration);
         }
 
-        (*buffers) = (*buffers)->toArrayMode(kMinOutputBufferArraySize);
         if (oStreamFormat.value == C2BufferData::LINEAR) {
             // 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(kMinOutputBufferArraySize);
 
             int32_t channelCount;
             int32_t sampleRate;
@@ -2340,33 +2232,6 @@
         }
     }
 
-    // Set up pipeline control. This has to be done after mInputBuffers and
-    // mOutputBuffers are initialized to make sure that lingering callbacks
-    // about buffers from the previous generation do not interfere with the
-    // newly initialized pipeline capacity.
-
-    // Query delays
-    C2PortRequestedDelayTuning::input inputDelay;
-    C2PortRequestedDelayTuning::output outputDelay;
-    C2RequestedPipelineDelayTuning pipelineDelay;
-#if 0
-    err = mComponent->query(
-            { &inputDelay, &pipelineDelay, &outputDelay },
-            {},
-            C2_DONT_BLOCK,
-            nullptr);
-    mAvailablePipelineCapacity.initialize(
-            inputDelay,
-            inputDelay + pipelineDelay,
-            inputDelay + pipelineDelay + outputDelay,
-            mName);
-#else
-    mAvailablePipelineCapacity.initialize(
-            kMinInputBufferArraySize,
-            kMaxPipelineCapacity,
-            mName);
-#endif
-
     mInputMetEos = false;
     mPendingEosTimestamp = INT64_MIN;
     mSync.start();
@@ -2441,6 +2306,7 @@
     for (const sp<MediaCodecBuffer> &buffer : toBeQueued) {
         if (queueInputBufferInternal(buffer) != OK) {
             mAvailablePipelineCapacity.freeComponentSlot("requestInitialInputBuffers");
+            mAvailablePipelineCapacity.freeOutputSlot("requestInitialInputBuffers");
         }
     }
     return OK;
@@ -2448,7 +2314,7 @@
 
 void CCodecBufferChannel::stop() {
     mSync.stop();
-    mFirstValidFrameIndex = mFrameIndex.load(std::memory_order_relaxed);
+    mFirstValidFrameIndex = mFrameIndex.load();
     if (mInputSurface != nullptr) {
         mInputSurface.reset();
     }
@@ -2491,11 +2357,23 @@
         std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
         const C2StreamInitDataInfo::output *initData,
         size_t numDiscardedInputBuffers) {
-    if (handleWork(std::move(work), outputFormat, initData)) {
-        mAvailablePipelineCapacity.freeInputSlots(numDiscardedInputBuffers,
-                                                  "onWorkDone");
-        feedInputBufferIfAvailable();
+    if (work->result == C2_NOT_FOUND) {
+        // TODO: Define what flushed work's result is.
+        ALOGD("[%s] flushed work; ignored.", mName);
+        return;
     }
+    if ((work->input.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
+        // Discard frames from previous generation.
+        ALOGD("[%s] Discard frames from previous generation.", mName);
+        return;
+    }
+
+    mAvailablePipelineCapacity.freeInputSlots(numDiscardedInputBuffers, "onWorkDone");
+    mAvailablePipelineCapacity.freeComponentSlot("onWorkDone");
+    if (handleWork(std::move(work), outputFormat, initData)) {
+        mAvailablePipelineCapacity.freeOutputSlot("onWorkDone");
+    }
+    feedInputBufferIfAvailable();
 }
 
 void CCodecBufferChannel::onInputBufferDone(
@@ -2504,11 +2382,9 @@
     {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
         newInputSlotAvailable = (*buffers)->expireComponentBuffer(buffer);
-        if (newInputSlotAvailable) {
-            mAvailablePipelineCapacity.freeInputSlots(1, "onInputBufferDone");
-        }
     }
     if (newInputSlotAvailable) {
+        mAvailablePipelineCapacity.freeInputSlots(1, "onInputBufferDone");
         feedInputBufferIfAvailable();
     }
 }
@@ -2517,23 +2393,6 @@
         std::unique_ptr<C2Work> work,
         const sp<AMessage> &outputFormat,
         const C2StreamInitDataInfo::output *initData) {
-    if ((work->input.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
-        // Discard frames from previous generation.
-        ALOGD("[%s] Discard frames from previous generation.", mName);
-        return false;
-    }
-
-    if (work->worklets.size() != 1u
-            || !work->worklets.front()
-            || !(work->worklets.front()->output.flags & C2FrameData::FLAG_INCOMPLETE)) {
-        mAvailablePipelineCapacity.freeComponentSlot("handleWork");
-    }
-
-    if (work->result == C2_NOT_FOUND) {
-        ALOGD("[%s] flushed work; ignored.", mName);
-        return true;
-    }
-
     if (work->result != C2_OK) {
         ALOGD("[%s] work failed to complete: %d", mName, work->result);
         mCCodecCallback->onError(work->result, ACTION_CODE_FATAL);
@@ -2563,40 +2422,6 @@
         }
     }
 
-    while (!worklet->output.configUpdate.empty()) {
-        std::unique_ptr<C2Param> param;
-        worklet->output.configUpdate.back().swap(param);
-        worklet->output.configUpdate.pop_back();
-        switch (param->coreIndex().coreIndex()) {
-            case C2PortReorderBufferDepthTuning::CORE_INDEX: {
-                C2PortReorderBufferDepthTuning::output reorderDepth;
-                if (reorderDepth.updateFrom(*param)) {
-                    mReorderStash.lock()->setDepth(reorderDepth.value);
-                    ALOGV("[%s] onWorkDone: updated reorder depth to %u",
-                          mName, reorderDepth.value);
-                } else {
-                    ALOGD("[%s] onWorkDone: failed to read reorder depth", mName);
-                }
-                break;
-            }
-            case C2PortReorderKeySetting::CORE_INDEX: {
-                C2PortReorderKeySetting::output reorderKey;
-                if (reorderKey.updateFrom(*param)) {
-                    mReorderStash.lock()->setKey(reorderKey.value);
-                    ALOGV("[%s] onWorkDone: updated reorder key to %u",
-                          mName, reorderKey.value);
-                } else {
-                    ALOGD("[%s] onWorkDone: failed to read reorder key", mName);
-                }
-                break;
-            }
-            default:
-                ALOGV("[%s] onWorkDone: unrecognized config update (%08X)",
-                      mName, param->index());
-                break;
-        }
-    }
-
     if (outputFormat != nullptr) {
         Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
         ALOGD("[%s] onWorkDone: output format changed to %s",
@@ -2621,6 +2446,7 @@
         ALOGV("[%s] onWorkDone: output EOS", mName);
     }
 
+    bool feedNeeded = true;
     sp<MediaCodecBuffer> outBuffer;
     size_t index;
 
@@ -2642,7 +2468,7 @@
 
     if (initData != nullptr) {
         Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if ((*buffers)->registerCsd(initData, &index, &outBuffer) == OK) {
+        if ((*buffers)->registerCsd(initData, &index, &outBuffer)) {
             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());
@@ -2650,6 +2476,7 @@
             buffers.unlock();
             mCallback->onOutputBufferAvailable(index, outBuffer);
             buffers.lock();
+            feedNeeded = false;
         } else {
             ALOGD("[%s] onWorkDone: unable to register csd", mName);
             buffers.unlock();
@@ -2662,7 +2489,7 @@
     if (!buffer && !flags) {
         ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
               mName, work->input.ordinal.frameIndex.peekull());
-        return true;
+        return feedNeeded;
     }
 
     if (buffer) {
@@ -2681,53 +2508,23 @@
     }
 
     {
-        Mutexed<ReorderStash>::Locked reorder(mReorderStash);
-        reorder->emplace(buffer, timestamp.peek(), flags, worklet->output.ordinal);
-        if (flags & MediaCodec::BUFFER_FLAG_EOS) {
-            // Flush reorder stash
-            reorder->setDepth(0);
-        }
-    }
-    sendOutputBuffers();
-    return true;
-}
-
-void CCodecBufferChannel::sendOutputBuffers() {
-    ReorderStash::Entry entry;
-    sp<MediaCodecBuffer> outBuffer;
-    size_t index;
-
-    while (true) {
-        {
-            Mutexed<ReorderStash>::Locked reorder(mReorderStash);
-            if (!reorder->hasPending()) {
-                break;
-            }
-            if (!reorder->pop(&entry)) {
-                break;
-            }
-        }
         Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        status_t err = (*buffers)->registerBuffer(entry.buffer, &index, &outBuffer);
-        if (err != OK) {
-            if (err != WOULD_BLOCK) {
-                OutputBuffersArray *array = (OutputBuffersArray *)buffers->get();
-                array->realloc(entry.buffer);
-                mCCodecCallback->onOutputBuffersChanged();
-            }
-            buffers.unlock();
-            ALOGV("[%s] sendOutputBuffers: unable to register output buffer", mName);
-            mReorderStash.lock()->defer(entry);
-            return;
+        if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
+            ALOGD("[%s] onWorkDone: unable to register output buffer", mName);
+            // TODO
+            // buffers.unlock();
+            // mCCodecCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            // buffers.lock();
+            return false;
         }
-        buffers.unlock();
-
-        outBuffer->meta()->setInt64("timeUs", entry.timestamp);
-        outBuffer->meta()->setInt32("flags", entry.flags);
-        ALOGV("[%s] sendOutputBuffers: out buffer index = %zu [%p] => %p + %zu",
-                mName, index, outBuffer.get(), outBuffer->data(), outBuffer->size());
-        mCallback->onOutputBufferAvailable(index, outBuffer);
     }
+
+    outBuffer->meta()->setInt64("timeUs", timestamp.peek());
+    outBuffer->meta()->setInt32("flags", flags);
+    ALOGV("[%s] onWorkDone: out buffer index = %zu [%p] => %p + %zu",
+            mName, index, outBuffer.get(), outBuffer->data(), outBuffer->size());
+    mCallback->onOutputBufferAvailable(index, outBuffer);
+    return false;
 }
 
 status_t CCodecBufferChannel::setSurface(const sp<Surface> &newSurface) {
diff --git a/media/sfplugin/CCodecBufferChannel.h b/media/sfplugin/CCodecBufferChannel.h
index e354fd0..86739ab 100644
--- a/media/sfplugin/CCodecBufferChannel.h
+++ b/media/sfplugin/CCodecBufferChannel.h
@@ -43,7 +43,6 @@
     virtual void onError(status_t err, enum ActionCode actionCode) = 0;
     virtual void onOutputFramesRendered(int64_t mediaTimeUs, nsecs_t renderTimeNs) = 0;
     virtual void onWorkQueued(bool eos) = 0;
-    virtual void onOutputBuffersChanged() = 0;
 };
 
 /**
@@ -218,7 +217,6 @@
     bool handleWork(
             std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
             const C2StreamInitDataInfo::output *initData);
-    void sendOutputBuffers();
 
     QueueSync mSync;
     sp<MemoryDealer> mDealer;
@@ -273,36 +271,44 @@
     //    CCodecBufferChannel whose outputs have not been returned from the
     //    component (by calling onWorkDone()) does not exceed a certain limit.
     //    (Let us call this the "component" capacity.)
+    // 3. The number of work items that have been received by
+    //    CCodecBufferChannel whose outputs have not been released by the app
+    //    (either by calling discardBuffer() on an output buffer or calling
+    //    renderOutputBuffer()) does not exceed a certain limit. (Let us call
+    //    this the "output" capacity.)
     //
     // These three criteria guarantee that a new input buffer that arrives from
     // the invocation of onInputBufferAvailable() will not
     // 1. overload CCodecBufferChannel's input buffers;
     // 2. overload the component; or
+    // 3. overload CCodecBufferChannel's output buffers if the component
+    //    finishes all the pending work right away.
     //
     struct PipelineCapacity {
         // The number of available input capacity.
         std::atomic_int input;
         // The number of available component capacity.
         std::atomic_int component;
+        // The number of available output capacity.
+        std::atomic_int output;
 
         PipelineCapacity();
-        // Set the values of #input and #component.
-        void initialize(int newInput, int newComponent,
+        // Set the values of #component and #output.
+        void initialize(int newInput, int newComponent, int newOutput,
                         const char* newName = "<UNKNOWN COMPONENT>",
                         const char* callerTag = nullptr);
 
-        // Return true and decrease #input and #component by one if
+        // Return true and decrease #input, #component and #output by one if
         // they are all greater than zero; return false otherwise.
         //
         // callerTag is used for logging only.
         //
         // allocate() is called by CCodecBufferChannel to check whether it can
         // receive another input buffer. If the return value is true,
-        // onInputBufferAvailable() and onOutputBufferAvailable() can be called
-        // afterwards.
+        // onInputBufferAvailable() can (and will) be called afterwards.
         bool allocate(const char* callerTag = nullptr);
 
-        // Increase #input and #component by one.
+        // Increase #input, #component and #output by one.
         //
         // callerTag is used for logging only.
         //
@@ -329,52 +335,21 @@
         // onWorkDone() is called.
         int freeComponentSlot(const char* callerTag = nullptr);
 
+        // Increase #output by one and return the updated value.
+        //
+        // callerTag is used for logging only.
+        //
+        // freeOutputSlot() is called by CCodecBufferChannel when
+        // discardBuffer() is called on an output buffer or when
+        // renderOutputBuffer() is called.
+        int freeOutputSlot(const char* callerTag = nullptr);
+
     private:
         // Component name. Used for logging.
         const char* mName;
     };
     PipelineCapacity mAvailablePipelineCapacity;
 
-    class ReorderStash {
-    public:
-        struct Entry {
-            inline Entry() : buffer(nullptr), timestamp(0), flags(0), ordinal({0, 0, 0}) {}
-            inline Entry(
-                    const std::shared_ptr<C2Buffer> &b,
-                    int64_t t,
-                    int32_t f,
-                    const C2WorkOrdinalStruct &o)
-                : buffer(b), timestamp(t), flags(f), ordinal(o) {}
-            std::shared_ptr<C2Buffer> buffer;
-            int64_t timestamp;
-            int32_t flags;
-            C2WorkOrdinalStruct ordinal;
-        };
-
-        ReorderStash();
-
-        void clear();
-        void setDepth(uint32_t depth);
-        void setKey(C2Config::ordinal_key_t key);
-        bool pop(Entry *entry);
-        void emplace(
-                const std::shared_ptr<C2Buffer> &buffer,
-                int64_t timestamp,
-                int32_t flags,
-                const C2WorkOrdinalStruct &ordinal);
-        void defer(const Entry &entry);
-        bool hasPending() const;
-
-    private:
-        std::list<Entry> mPending;
-        std::list<Entry> mStash;
-        uint32_t mDepth;
-        C2Config::ordinal_key_t mKey;
-
-        bool less(const C2WorkOrdinalStruct &o1, const C2WorkOrdinalStruct &o2);
-    };
-    Mutexed<ReorderStash> mReorderStash;
-
     std::atomic_bool mInputMetEos;
     std::atomic_int64_t mPendingEosTimestamp;