CCodec: move Buffers out into a separate file

Seperate Buffers out before we add complex logic to them. Also add
missing documentation.

Bug: 130223947
Test: builds
Change-Id: I8f992cb36499401ee2c6d729aa52f3888a7bcea5
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 66457e7..8ae80ee 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -5,6 +5,7 @@
         "C2OMXNode.cpp",
         "CCodec.cpp",
         "CCodecBufferChannel.cpp",
+        "CCodecBuffers.cpp",
         "CCodecConfig.cpp",
         "Codec2Buffer.cpp",
         "Codec2InfoBuilder.cpp",
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index d1fa920..00e0c16 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -56,1398 +56,10 @@
 
 using CasStatus = hardware::cas::V1_0::Status;
 
-/**
- * Base class for representation of buffers at one port.
- */
-class CCodecBufferChannel::Buffers {
-public:
-    Buffers(const char *componentName, const char *name = "Buffers")
-        : mComponentName(componentName),
-          mChannelName(std::string(componentName) + ":" + name),
-          mName(mChannelName.c_str()) {
-    }
-    virtual ~Buffers() = default;
-
-    /**
-     * Set format for MediaCodec-facing buffers.
-     */
-    void setFormat(const sp<AMessage> &format) {
-        CHECK(format != nullptr);
-        mFormat = format;
-    }
-
-    /**
-     * Return a copy of current format.
-     */
-    sp<AMessage> dupFormat() {
-        return mFormat != nullptr ? mFormat->dup() : nullptr;
-    }
-
-    /**
-     * Returns true if the buffers are operating under array mode.
-     */
-    virtual bool isArrayMode() const { return false; }
-
-    /**
-     * Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
-     * no-op.
-     */
-    virtual void getArray(Vector<sp<MediaCodecBuffer>> *) const {}
-
-    /**
-     * Return number of buffers the client owns.
-     */
-    virtual size_t numClientBuffers() const = 0;
-
-    void handleImageData(const sp<Codec2Buffer> &buffer) {
-        sp<ABuffer> imageDataCandidate = buffer->getImageData();
-        if (imageDataCandidate == nullptr) {
-            return;
-        }
-        sp<ABuffer> imageData;
-        if (!mFormat->findBuffer("image-data", &imageData)
-                || imageDataCandidate->size() != imageData->size()
-                || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) {
-            ALOGD("[%s] updating image-data", mName);
-            sp<AMessage> newFormat = dupFormat();
-            newFormat->setBuffer("image-data", imageDataCandidate);
-            MediaImage2 *img = (MediaImage2*)imageDataCandidate->data();
-            if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) {
-                int32_t stride = img->mPlane[0].mRowInc;
-                newFormat->setInt32(KEY_STRIDE, stride);
-                ALOGD("[%s] updating stride = %d", mName, stride);
-                if (img->mNumPlanes > 1 && stride > 0) {
-                    int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride;
-                    newFormat->setInt32(KEY_SLICE_HEIGHT, vstride);
-                    ALOGD("[%s] updating vstride = %d", mName, vstride);
-                }
-            }
-            setFormat(newFormat);
-            buffer->setFormat(newFormat);
-        }
-    }
-
-protected:
-    std::string mComponentName; ///< name of component for debugging
-    std::string mChannelName; ///< name of channel for debugging
-    const char *mName; ///< C-string version of channel name
-    // Format to be used for creating MediaCodec-facing buffers.
-    sp<AMessage> mFormat;
-
-private:
-    DISALLOW_EVIL_CONSTRUCTORS(Buffers);
-};
-
-class CCodecBufferChannel::InputBuffers : public CCodecBufferChannel::Buffers {
-public:
-    InputBuffers(const char *componentName, const char *name = "Input[]")
-        : Buffers(componentName, name) { }
-    virtual ~InputBuffers() = default;
-
-    /**
-     * Set a block pool to obtain input memory blocks.
-     */
-    void setPool(const std::shared_ptr<C2BlockPool> &pool) { mPool = pool; }
-
-    /**
-     * Get a new MediaCodecBuffer for input and its corresponding index.
-     * Returns false if no new buffer can be obtained at the moment.
-     */
-    virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
-
-    /**
-     * Release the buffer obtained from requestNewBuffer() and get the
-     * associated C2Buffer object back. Returns true if the buffer was on file
-     * and released successfully.
-     */
-    virtual bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) = 0;
-
-    /**
-     * Release the buffer that is no longer used by the codec process. Return
-     * true if and only if the buffer was on file and released successfully.
-     */
-    virtual bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) = 0;
-
-    /**
-     * Flush internal state. After this call, no index or buffer previously
-     * returned from requestNewBuffer() is valid.
-     */
-    virtual void flush() = 0;
-
-    /**
-     * Return array-backed version of input buffers. The returned object
-     * shall retain the internal state so that it will honor index and
-     * buffer from previous calls of requestNewBuffer().
-     */
-    virtual std::unique_ptr<InputBuffers> toArrayMode(size_t size) = 0;
-
-protected:
-    // Pool to obtain blocks for input buffers.
-    std::shared_ptr<C2BlockPool> mPool;
-
-private:
-    DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
-};
-
-class CCodecBufferChannel::OutputBuffers : public CCodecBufferChannel::Buffers {
-public:
-    OutputBuffers(const char *componentName, const char *name = "Output")
-        : Buffers(componentName, name) { }
-    virtual ~OutputBuffers() = default;
-
-    /**
-     * Register output C2Buffer from the component and obtain corresponding
-     * index and MediaCodecBuffer object. Returns false if registration
-     * fails.
-     */
-    virtual status_t registerBuffer(
-            const std::shared_ptr<C2Buffer> &buffer,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) = 0;
-
-    /**
-     * Register codec specific data as a buffer to be consistent with
-     * MediaCodec behavior.
-     */
-    virtual status_t registerCsd(
-            const C2StreamInitDataInfo::output * /* csd */,
-            size_t * /* index */,
-            sp<MediaCodecBuffer> * /* clientBuffer */) = 0;
-
-    /**
-     * Release the buffer obtained from registerBuffer() and get the
-     * associated C2Buffer object back. Returns true if the buffer was on file
-     * and released successfully.
-     */
-    virtual bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) = 0;
-
-    /**
-     * Flush internal state. After this call, no index or buffer previously
-     * returned from registerBuffer() is valid.
-     */
-    virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) = 0;
-
-    /**
-     * Return array-backed version of output buffers. The returned object
-     * shall retain the internal state so that it will honor index and
-     * buffer from previous calls of registerBuffer().
-     */
-    virtual std::unique_ptr<OutputBuffers> toArrayMode(size_t size) = 0;
-
-    /**
-     * Initialize SkipCutBuffer object.
-     */
-    void initSkipCutBuffer(
-            int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount) {
-        CHECK(mSkipCutBuffer == nullptr);
-        mDelay = delay;
-        mPadding = padding;
-        mSampleRate = sampleRate;
-        setSkipCutBuffer(delay, padding, channelCount);
-    }
-
-    /**
-     * Update the SkipCutBuffer object. No-op if it's never initialized.
-     */
-    void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount) {
-        if (mSkipCutBuffer == nullptr) {
-            return;
-        }
-        int32_t delay = mDelay;
-        int32_t padding = mPadding;
-        if (sampleRate != mSampleRate) {
-            delay = ((int64_t)delay * sampleRate) / mSampleRate;
-            padding = ((int64_t)padding * sampleRate) / mSampleRate;
-        }
-        setSkipCutBuffer(delay, padding, channelCount);
-    }
-
-    /**
-     * Submit buffer to SkipCutBuffer object, if initialized.
-     */
-    void submit(const sp<MediaCodecBuffer> &buffer) {
-        if (mSkipCutBuffer != nullptr) {
-            mSkipCutBuffer->submit(buffer);
-        }
-    }
-
-    /**
-     * Transfer SkipCutBuffer object to the other Buffers object.
-     */
-    void transferSkipCutBuffer(const sp<SkipCutBuffer> &scb) {
-        mSkipCutBuffer = scb;
-    }
-
-protected:
-    sp<SkipCutBuffer> mSkipCutBuffer;
-
-private:
-    int32_t mDelay;
-    int32_t mPadding;
-    int32_t mSampleRate;
-
-    void setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount) {
-        if (mSkipCutBuffer != nullptr) {
-            size_t prevSize = mSkipCutBuffer->size();
-            if (prevSize != 0u) {
-                ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize);
-            }
-        }
-        mSkipCutBuffer = new SkipCutBuffer(skip, cut, channelCount);
-    }
-
-    DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
-};
-
 namespace {
 
-const static size_t kSmoothnessFactor = 4;
-const static size_t kRenderingDepth = 3;
-const static size_t kLinearBufferSize = 1048576;
-// This can fit 4K RGBA frame, and most likely client won't need more than this.
-const static size_t kMaxLinearBufferSize = 3840 * 2160 * 4;
-
-/**
- * Simple local buffer pool backed by std::vector.
- */
-class LocalBufferPool : public std::enable_shared_from_this<LocalBufferPool> {
-public:
-    /**
-     * Create a new LocalBufferPool object.
-     *
-     * \param poolCapacity  max total size of buffers managed by this pool.
-     *
-     * \return  a newly created pool object.
-     */
-    static std::shared_ptr<LocalBufferPool> Create(size_t poolCapacity) {
-        return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(poolCapacity));
-    }
-
-    /**
-     * Return an ABuffer object whose size is at least |capacity|.
-     *
-     * \param   capacity  requested capacity
-     * \return  nullptr if the pool capacity is reached
-     *          an ABuffer object otherwise.
-     */
-    sp<ABuffer> newBuffer(size_t capacity) {
-        Mutex::Autolock lock(mMutex);
-        auto it = std::find_if(
-                mPool.begin(), mPool.end(),
-                [capacity](const std::vector<uint8_t> &vec) {
-                    return vec.capacity() >= capacity;
-                });
-        if (it != mPool.end()) {
-            sp<ABuffer> buffer = new VectorBuffer(std::move(*it), shared_from_this());
-            mPool.erase(it);
-            return buffer;
-        }
-        if (mUsedSize + capacity > mPoolCapacity) {
-            while (!mPool.empty()) {
-                mUsedSize -= mPool.back().capacity();
-                mPool.pop_back();
-            }
-            if (mUsedSize + capacity > mPoolCapacity) {
-                ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
-                        mUsedSize, capacity, mPoolCapacity);
-                return nullptr;
-            }
-        }
-        std::vector<uint8_t> vec(capacity);
-        mUsedSize += vec.capacity();
-        return new VectorBuffer(std::move(vec), shared_from_this());
-    }
-
-private:
-    /**
-     * ABuffer backed by std::vector.
-     */
-    class VectorBuffer : public ::android::ABuffer {
-    public:
-        /**
-         * Construct a VectorBuffer by taking the ownership of supplied vector.
-         *
-         * \param vec   backing vector of the buffer. this object takes
-         *              ownership at construction.
-         * \param pool  a LocalBufferPool object to return the vector at
-         *              destruction.
-         */
-        VectorBuffer(std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool)
-            : ABuffer(vec.data(), vec.capacity()),
-              mVec(std::move(vec)),
-              mPool(pool) {
-        }
-
-        ~VectorBuffer() override {
-            std::shared_ptr<LocalBufferPool> pool = mPool.lock();
-            if (pool) {
-                // If pool is alive, return the vector back to the pool so that
-                // it can be recycled.
-                pool->returnVector(std::move(mVec));
-            }
-        }
-
-    private:
-        std::vector<uint8_t> mVec;
-        std::weak_ptr<LocalBufferPool> mPool;
-    };
-
-    Mutex mMutex;
-    size_t mPoolCapacity;
-    size_t mUsedSize;
-    std::list<std::vector<uint8_t>> mPool;
-
-    /**
-     * Private constructor to prevent constructing non-managed LocalBufferPool.
-     */
-    explicit LocalBufferPool(size_t poolCapacity)
-        : mPoolCapacity(poolCapacity), mUsedSize(0) {
-    }
-
-    /**
-     * Take back the ownership of vec from the destructed VectorBuffer and put
-     * it in front of the pool.
-     */
-    void returnVector(std::vector<uint8_t> &&vec) {
-        Mutex::Autolock lock(mMutex);
-        mPool.push_front(std::move(vec));
-    }
-
-    DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool);
-};
-
-sp<GraphicBlockBuffer> AllocateGraphicBuffer(
-        const std::shared_ptr<C2BlockPool> &pool,
-        const sp<AMessage> &format,
-        uint32_t pixelFormat,
-        const C2MemoryUsage &usage,
-        const std::shared_ptr<LocalBufferPool> &localBufferPool) {
-    int32_t width, height;
-    if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) {
-        ALOGD("format lacks width or height");
-        return nullptr;
-    }
-
-    std::shared_ptr<C2GraphicBlock> block;
-    c2_status_t err = pool->fetchGraphicBlock(
-            width, height, pixelFormat, usage, &block);
-    if (err != C2_OK) {
-        ALOGD("fetch graphic block failed: %d", err);
-        return nullptr;
-    }
-
-    return GraphicBlockBuffer::Allocate(
-            format,
-            block,
-            [localBufferPool](size_t capacity) {
-                return localBufferPool->newBuffer(capacity);
-            });
-}
-
-class BuffersArrayImpl;
-
-/**
- * Flexible buffer slots implementation.
- */
-class FlexBuffersImpl {
-public:
-    FlexBuffersImpl(const char *name)
-        : mImplName(std::string(name) + ".Impl"),
-          mName(mImplName.c_str()) { }
-
-    /**
-     * Assign an empty slot for a buffer and return the index. If there's no
-     * empty slot, just add one at the end and return it.
-     *
-     * \param buffer[in]  a new buffer to assign a slot.
-     * \return            index of the assigned slot.
-     */
-    size_t assignSlot(const sp<Codec2Buffer> &buffer) {
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            if (mBuffers[i].clientBuffer == nullptr
-                    && mBuffers[i].compBuffer.expired()) {
-                mBuffers[i].clientBuffer = buffer;
-                return i;
-            }
-        }
-        mBuffers.push_back({ buffer, std::weak_ptr<C2Buffer>() });
-        return mBuffers.size() - 1;
-    }
-
-    /**
-     * Release the slot from the client, and get the C2Buffer object back from
-     * the previously assigned buffer. Note that the slot is not completely free
-     * until the returned C2Buffer object is freed.
-     *
-     * \param   buffer[in]        the buffer previously assigned a slot.
-     * \param   c2buffer[in,out]  pointer to C2Buffer to be populated. Ignored
-     *                            if null.
-     * \return  true  if the buffer is successfully released from a slot
-     *          false otherwise
-     */
-    bool releaseSlot(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) {
-        sp<Codec2Buffer> clientBuffer;
-        size_t index = mBuffers.size();
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            if (mBuffers[i].clientBuffer == buffer) {
-                clientBuffer = mBuffers[i].clientBuffer;
-                if (release) {
-                    mBuffers[i].clientBuffer.clear();
-                }
-                index = i;
-                break;
-            }
-        }
-        if (clientBuffer == nullptr) {
-            ALOGV("[%s] %s: No matching buffer found", mName, __func__);
-            return false;
-        }
-        std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
-        if (!result) {
-            result = clientBuffer->asC2Buffer();
-            mBuffers[index].compBuffer = result;
-        }
-        if (c2buffer) {
-            *c2buffer = result;
-        }
-        return true;
-    }
-
-    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            std::shared_ptr<C2Buffer> compBuffer =
-                    mBuffers[i].compBuffer.lock();
-            if (!compBuffer || compBuffer != c2buffer) {
-                continue;
-            }
-            mBuffers[i].compBuffer.reset();
-            ALOGV("[%s] codec released buffer #%zu", mName, i);
-            return true;
-        }
-        ALOGV("[%s] codec released an unknown buffer", mName);
-        return false;
-    }
-
-    void flush() {
-        ALOGV("[%s] buffers are flushed %zu", mName, mBuffers.size());
-        mBuffers.clear();
-    }
-
-    size_t numClientBuffers() const {
-        return std::count_if(
-                mBuffers.begin(), mBuffers.end(),
-                [](const Entry &entry) {
-                    return (entry.clientBuffer != nullptr);
-                });
-    }
-
-private:
-    friend class BuffersArrayImpl;
-
-    std::string mImplName; ///< name for debugging
-    const char *mName; ///< C-string version of name
-
-    struct Entry {
-        sp<Codec2Buffer> clientBuffer;
-        std::weak_ptr<C2Buffer> compBuffer;
-    };
-    std::vector<Entry> mBuffers;
-};
-
-/**
- * Static buffer slots implementation based on a fixed-size array.
- */
-class BuffersArrayImpl {
-public:
-    BuffersArrayImpl()
-        : mImplName("BuffersArrayImpl"),
-          mName(mImplName.c_str()) { }
-
-    /**
-     * Initialize buffer array from the original |impl|. The buffers known by
-     * the client is preserved, and the empty slots are populated so that the
-     * array size is at least |minSize|.
-     *
-     * \param impl[in]      FlexBuffersImpl object used so far.
-     * \param minSize[in]   minimum size of the buffer array.
-     * \param allocate[in]  function to allocate a client buffer for an empty slot.
-     */
-    void initialize(
-            const FlexBuffersImpl &impl,
-            size_t minSize,
-            std::function<sp<Codec2Buffer>()> allocate) {
-        mImplName = impl.mImplName + "[N]";
-        mName = mImplName.c_str();
-        for (size_t i = 0; i < impl.mBuffers.size(); ++i) {
-            sp<Codec2Buffer> clientBuffer = impl.mBuffers[i].clientBuffer;
-            bool ownedByClient = (clientBuffer != nullptr);
-            if (!ownedByClient) {
-                clientBuffer = allocate();
-            }
-            mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient });
-        }
-        ALOGV("[%s] converted %zu buffers to array mode of %zu", mName, mBuffers.size(), minSize);
-        for (size_t i = impl.mBuffers.size(); i < minSize; ++i) {
-            mBuffers.push_back({ allocate(), std::weak_ptr<C2Buffer>(), false });
-        }
-    }
-
-    /**
-     * Grab a buffer from the underlying array which matches the criteria.
-     *
-     * \param index[out]    index of the slot.
-     * \param buffer[out]   the matching buffer.
-     * \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
-     */
-    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;
-            }
-        }
-        return allBuffersDontMatch ? NO_MEMORY : WOULD_BLOCK;
-    }
-
-    /**
-     * Return the buffer from the client, and get the C2Buffer object back from
-     * the buffer. Note that the slot is not completely free until the returned
-     * C2Buffer object is freed.
-     *
-     * \param   buffer[in]        the buffer previously grabbed.
-     * \param   c2buffer[in,out]  pointer to C2Buffer to be populated. Ignored
-     *                            if null.
-     * \return  true  if the buffer is successfully returned
-     *          false otherwise
-     */
-    bool returnBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) {
-        sp<Codec2Buffer> clientBuffer;
-        size_t index = mBuffers.size();
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            if (mBuffers[i].clientBuffer == buffer) {
-                if (!mBuffers[i].ownedByClient) {
-                    ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu", mName, i);
-                }
-                clientBuffer = mBuffers[i].clientBuffer;
-                if (release) {
-                    mBuffers[i].ownedByClient = false;
-                }
-                index = i;
-                break;
-            }
-        }
-        if (clientBuffer == nullptr) {
-            ALOGV("[%s] %s: No matching buffer found", mName, __func__);
-            return false;
-        }
-        ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index);
-        std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
-        if (!result) {
-            result = clientBuffer->asC2Buffer();
-            mBuffers[index].compBuffer = result;
-        }
-        if (c2buffer) {
-            *c2buffer = result;
-        }
-        return true;
-    }
-
-    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            std::shared_ptr<C2Buffer> compBuffer =
-                    mBuffers[i].compBuffer.lock();
-            if (!compBuffer) {
-                continue;
-            }
-            if (c2buffer == compBuffer) {
-                if (mBuffers[i].ownedByClient) {
-                    // This should not happen.
-                    ALOGD("[%s] codec released a buffer owned by client "
-                          "(index %zu)", mName, i);
-                }
-                mBuffers[i].compBuffer.reset();
-                ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i);
-                return true;
-            }
-        }
-        ALOGV("[%s] codec released an unknown buffer (array mode)", mName);
-        return false;
-    }
-
-    /**
-     * Populate |array| with the underlying buffer array.
-     *
-     * \param array[out]  an array to be filled with the underlying buffer array.
-     */
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) const {
-        array->clear();
-        for (const Entry &entry : mBuffers) {
-            array->push(entry.clientBuffer);
-        }
-    }
-
-    /**
-     * The client abandoned all known buffers, so reclaim the ownership.
-     */
-    void flush() {
-        for (Entry &entry : mBuffers) {
-            entry.ownedByClient = false;
-        }
-    }
-
-    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 });
-        }
-    }
-
-    size_t numClientBuffers() const {
-        return std::count_if(
-                mBuffers.begin(), mBuffers.end(),
-                [](const Entry &entry) {
-                    return entry.ownedByClient;
-                });
-    }
-
-private:
-    std::string mImplName; ///< name for debugging
-    const char *mName; ///< C-string version of name
-
-    struct Entry {
-        const sp<Codec2Buffer> clientBuffer;
-        std::weak_ptr<C2Buffer> compBuffer;
-        bool ownedByClient;
-    };
-    std::vector<Entry> mBuffers;
-};
-
-class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
-public:
-    InputBuffersArray(const char *componentName, const char *name = "Input[N]")
-        : InputBuffers(componentName, name) { }
-    ~InputBuffersArray() override = default;
-
-    void initialize(
-            const FlexBuffersImpl &impl,
-            size_t minSize,
-            std::function<sp<Codec2Buffer>()> allocate) {
-        mImpl.initialize(impl, minSize, allocate);
-    }
-
-    bool isArrayMode() const final { return true; }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t) final {
-        return nullptr;
-    }
-
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
-        mImpl.getArray(array);
-    }
-
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        sp<Codec2Buffer> c2Buffer;
-        status_t err = mImpl.grabBuffer(index, &c2Buffer);
-        if (err == OK) {
-            c2Buffer->setFormat(mFormat);
-            handleImageData(c2Buffer);
-            *buffer = c2Buffer;
-            return true;
-        }
-        return false;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) override {
-        return mImpl.returnBuffer(buffer, c2buffer, release);
-    }
-
-    bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) override {
-        return mImpl.expireComponentBuffer(c2buffer);
-    }
-
-    void flush() override {
-        mImpl.flush();
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-private:
-    BuffersArrayImpl mImpl;
-};
-
-class LinearInputBuffers : public CCodecBufferChannel::InputBuffers {
-public:
-    LinearInputBuffers(const char *componentName, const char *name = "1D-Input")
-        : InputBuffers(componentName, name),
-          mImpl(mName) { }
-
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        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);
-        if (newBuffer == nullptr) {
-            return false;
-        }
-        *index = mImpl.assignSlot(newBuffer);
-        *buffer = newBuffer;
-        return true;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) override {
-        return mImpl.releaseSlot(buffer, c2buffer, release);
-    }
-
-    bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) override {
-        return mImpl.expireComponentBuffer(c2buffer);
-    }
-
-    void flush() override {
-        // This is no-op by default unless we're in array mode where we need to keep
-        // track of the flushed work.
-        mImpl.flush();
-    }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t size) final {
-        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<InputBuffersArray> array(
-                new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]"));
-        array->setPool(mPool);
-        array->setFormat(mFormat);
-        array->initialize(
-                mImpl,
-                size,
-                [this, capacity] () -> sp<Codec2Buffer> { return alloc(capacity); });
-        return std::move(array);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-    virtual sp<Codec2Buffer> alloc(size_t size) {
-        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        std::shared_ptr<C2LinearBlock> block;
-
-        c2_status_t err = mPool->fetchLinearBlock(size, usage, &block);
-        if (err != C2_OK) {
-            return nullptr;
-        }
-
-        return LinearBlockBuffer::Allocate(mFormat, block);
-    }
-
-private:
-    FlexBuffersImpl mImpl;
-};
-
-class EncryptedLinearInputBuffers : public LinearInputBuffers {
-public:
-    EncryptedLinearInputBuffers(
-            bool secure,
-            const sp<MemoryDealer> &dealer,
-            const sp<ICrypto> &crypto,
-            int32_t heapSeqNum,
-            size_t capacity,
-            size_t numInputSlots,
-            const char *componentName, const char *name = "EncryptedInput")
-        : LinearInputBuffers(componentName, name),
-          mUsage({0, 0}),
-          mDealer(dealer),
-          mCrypto(crypto),
-          mHeapSeqNum(heapSeqNum) {
-        if (secure) {
-            mUsage = { C2MemoryUsage::READ_PROTECTED, 0 };
-        } else {
-            mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        }
-        for (size_t i = 0; i < numInputSlots; ++i) {
-            sp<IMemory> memory = mDealer->allocate(capacity);
-            if (memory == nullptr) {
-                ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated", mName, i);
-                break;
-            }
-            mMemoryVector.push_back({std::weak_ptr<C2LinearBlock>(), memory});
-        }
-    }
-
-    ~EncryptedLinearInputBuffers() override {
-    }
-
-    sp<Codec2Buffer> alloc(size_t size) override {
-        sp<IMemory> memory;
-        size_t slot = 0;
-        for (; slot < mMemoryVector.size(); ++slot) {
-            if (mMemoryVector[slot].block.expired()) {
-                memory = mMemoryVector[slot].memory;
-                break;
-            }
-        }
-        if (memory == nullptr) {
-            return nullptr;
-        }
-
-        std::shared_ptr<C2LinearBlock> block;
-        c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block);
-        if (err != C2_OK || block == nullptr) {
-            return nullptr;
-        }
-
-        mMemoryVector[slot].block = block;
-        return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum);
-    }
-
-private:
-    C2MemoryUsage mUsage;
-    sp<MemoryDealer> mDealer;
-    sp<ICrypto> mCrypto;
-    int32_t mHeapSeqNum;
-    struct Entry {
-        std::weak_ptr<C2LinearBlock> block;
-        sp<IMemory> memory;
-    };
-    std::vector<Entry> mMemoryVector;
-};
-
-class GraphicMetadataInputBuffers : public CCodecBufferChannel::InputBuffers {
-public:
-    GraphicMetadataInputBuffers(const char *componentName, const char *name = "2D-MetaInput")
-        : InputBuffers(componentName, name),
-          mImpl(mName),
-          mStore(GetCodec2PlatformAllocatorStore()) { }
-    ~GraphicMetadataInputBuffers() override = default;
-
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        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);
-        if (newBuffer == nullptr) {
-            return false;
-        }
-        *index = mImpl.assignSlot(newBuffer);
-        *buffer = newBuffer;
-        return true;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) override {
-        return mImpl.releaseSlot(buffer, c2buffer, release);
-    }
-
-    bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) override {
-        return mImpl.expireComponentBuffer(c2buffer);
-    }
-
-    void flush() override {
-        // This is no-op by default unless we're in array mode where we need to keep
-        // track of the flushed work.
-    }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t size) final {
-        std::shared_ptr<C2Allocator> alloc;
-        c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
-        if (err != C2_OK) {
-            return nullptr;
-        }
-        std::unique_ptr<InputBuffersArray> array(
-                new InputBuffersArray(mComponentName.c_str(), "2D-MetaInput[N]"));
-        array->setPool(mPool);
-        array->setFormat(mFormat);
-        array->initialize(
-                mImpl,
-                size,
-                [format = mFormat, alloc]() -> sp<Codec2Buffer> {
-                    return new GraphicMetadataBuffer(format, alloc);
-                });
-        return std::move(array);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-private:
-    FlexBuffersImpl mImpl;
-    std::shared_ptr<C2AllocatorStore> mStore;
-};
-
-class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
-public:
-    GraphicInputBuffers(
-            size_t numInputSlots, const char *componentName, const char *name = "2D-BB-Input")
-        : InputBuffers(componentName, name),
-          mImpl(mName),
-          mLocalBufferPool(LocalBufferPool::Create(
-                  kMaxLinearBufferSize * numInputSlots)) { }
-    ~GraphicInputBuffers() override = default;
-
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        // 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);
-        if (newBuffer == nullptr) {
-            return false;
-        }
-        *index = mImpl.assignSlot(newBuffer);
-        handleImageData(newBuffer);
-        *buffer = newBuffer;
-        return true;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) override {
-        return mImpl.releaseSlot(buffer, c2buffer, release);
-    }
-
-    bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) override {
-        return mImpl.expireComponentBuffer(c2buffer);
-    }
-
-    void flush() override {
-        // This is no-op by default unless we're in array mode where we need to keep
-        // track of the flushed work.
-    }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t size) final {
-        std::unique_ptr<InputBuffersArray> array(
-                new InputBuffersArray(mComponentName.c_str(), "2D-BB-Input[N]"));
-        array->setPool(mPool);
-        array->setFormat(mFormat);
-        array->initialize(
-                mImpl,
-                size,
-                [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp<Codec2Buffer> {
-                    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-                    return AllocateGraphicBuffer(
-                            pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp);
-                });
-        return std::move(array);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-private:
-    FlexBuffersImpl mImpl;
-    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
-};
-
-class DummyInputBuffers : public CCodecBufferChannel::InputBuffers {
-public:
-    DummyInputBuffers(const char *componentName, const char *name = "2D-Input")
-        : InputBuffers(componentName, name) { }
-
-    bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
-        return false;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &, std::shared_ptr<C2Buffer> *, bool) override {
-        return false;
-    }
-
-    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &) override {
-        return false;
-    }
-    void flush() override {
-    }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t) final {
-        return nullptr;
-    }
-
-    bool isArrayMode() const final { return true; }
-
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
-        array->clear();
-    }
-
-    size_t numClientBuffers() const final {
-        return 0u;
-    }
-};
-
-class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
-public:
-    OutputBuffersArray(const char *componentName, const char *name = "Output[N]")
-        : OutputBuffers(componentName, name) { }
-    ~OutputBuffersArray() override = default;
-
-    void initialize(
-            const FlexBuffersImpl &impl,
-            size_t minSize,
-            std::function<sp<Codec2Buffer>()> allocate) {
-        mImpl.initialize(impl, minSize, allocate);
-    }
-
-    bool isArrayMode() const final { return true; }
-
-    std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode(
-            size_t) final {
-        return nullptr;
-    }
-
-    status_t registerBuffer(
-            const std::shared_ptr<C2Buffer> &buffer,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) final {
-        sp<Codec2Buffer> c2Buffer;
-        status_t err = mImpl.grabBuffer(
-                index,
-                &c2Buffer,
-                [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) {
-            ALOGD("[%s] grabBuffer failed: %d", mName, err);
-            return err;
-        }
-        c2Buffer->setFormat(mFormat);
-        if (!c2Buffer->copy(buffer)) {
-            ALOGD("[%s] copy buffer failed", mName);
-            return WOULD_BLOCK;
-        }
-        submit(c2Buffer);
-        handleImageData(c2Buffer);
-        *clientBuffer = c2Buffer;
-        ALOGV("[%s] grabbed buffer %zu", mName, *index);
-        return OK;
-    }
-
-    status_t registerCsd(
-            const C2StreamInitDataInfo::output *csd,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) final {
-        sp<Codec2Buffer> c2Buffer;
-        status_t err = mImpl.grabBuffer(
-                index,
-                &c2Buffer,
-                [csd](const sp<Codec2Buffer> &clientBuffer) {
-                    return clientBuffer->base() != nullptr
-                            && clientBuffer->capacity() >= csd->flexCount();
-                });
-        if (err != OK) {
-            return err;
-        }
-        memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
-        c2Buffer->setRange(0, csd->flexCount());
-        c2Buffer->setFormat(mFormat);
-        *clientBuffer = c2Buffer;
-        return OK;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.returnBuffer(buffer, c2buffer, true);
-    }
-
-    void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
-        (void)flushedWork;
-        mImpl.flush();
-        if (mSkipCutBuffer != nullptr) {
-            mSkipCutBuffer->clear();
-        }
-    }
-
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
-        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);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-private:
-    BuffersArrayImpl mImpl;
-};
-
-class FlexOutputBuffers : public CCodecBufferChannel::OutputBuffers {
-public:
-    FlexOutputBuffers(const char *componentName, const char *name = "Output[]")
-        : OutputBuffers(componentName, name),
-          mImpl(mName) { }
-
-    status_t registerBuffer(
-            const std::shared_ptr<C2Buffer> &buffer,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) override {
-        sp<Codec2Buffer> newBuffer = wrap(buffer);
-        if (newBuffer == nullptr) {
-            return NO_MEMORY;
-        }
-        newBuffer->setFormat(mFormat);
-        *index = mImpl.assignSlot(newBuffer);
-        handleImageData(newBuffer);
-        *clientBuffer = newBuffer;
-        ALOGV("[%s] registered buffer %zu", mName, *index);
-        return OK;
-    }
-
-    status_t registerCsd(
-            const C2StreamInitDataInfo::output *csd,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) final {
-        sp<Codec2Buffer> newBuffer = new LocalLinearBuffer(
-                mFormat, ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
-        *index = mImpl.assignSlot(newBuffer);
-        *clientBuffer = newBuffer;
-        return OK;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.releaseSlot(buffer, c2buffer, true);
-    }
-
-    void flush(
-            const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
-        (void) flushedWork;
-        // This is no-op by default unless we're in array mode where we need to keep
-        // track of the flushed work.
-    }
-
-    std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode(
-            size_t size) override {
-        std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray(mComponentName.c_str()));
-        array->setFormat(mFormat);
-        array->transferSkipCutBuffer(mSkipCutBuffer);
-        array->initialize(
-                mImpl,
-                size,
-                [this]() { return allocateArrayBuffer(); });
-        return std::move(array);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-    /**
-     * Return an appropriate Codec2Buffer object for the type of buffers.
-     *
-     * \param buffer  C2Buffer object to wrap.
-     *
-     * \return  appropriate Codec2Buffer object to wrap |buffer|.
-     */
-    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  appropriate Codec2Buffer object which can copy() from C2Buffers.
-     */
-    virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
-
-private:
-    FlexBuffersImpl mImpl;
-};
-
-class LinearOutputBuffers : public FlexOutputBuffers {
-public:
-    LinearOutputBuffers(const char *componentName, const char *name = "1D-Output")
-        : FlexOutputBuffers(componentName, name) { }
-
-    void flush(
-            const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
-        if (mSkipCutBuffer != nullptr) {
-            mSkipCutBuffer->clear();
-        }
-        FlexOutputBuffers::flush(flushedWork);
-    }
-
-    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
-        if (buffer == nullptr) {
-            ALOGV("[%s] using a dummy buffer", mName);
-            return new LocalLinearBuffer(mFormat, new ABuffer(0));
-        }
-        if (buffer->data().type() != C2BufferData::LINEAR) {
-            ALOGV("[%s] non-linear buffer %d", mName, buffer->data().type());
-            // We expect linear output buffers from the component.
-            return nullptr;
-        }
-        if (buffer->data().linearBlocks().size() != 1u) {
-            ALOGV("[%s] no linear buffers", mName);
-            // We expect one and only one linear block from the component.
-            return nullptr;
-        }
-        sp<Codec2Buffer> clientBuffer = ConstLinearBlockBuffer::Allocate(mFormat, buffer);
-        if (clientBuffer == nullptr) {
-            ALOGD("[%s] ConstLinearBlockBuffer::Allocate failed", mName);
-            return nullptr;
-        }
-        submit(clientBuffer);
-        return clientBuffer;
-    }
-
-    sp<Codec2Buffer> allocateArrayBuffer() override {
-        // TODO: proper max output size
-        return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
-    }
-};
-
-class GraphicOutputBuffers : public FlexOutputBuffers {
-public:
-    GraphicOutputBuffers(const char *componentName, const char *name = "2D-Output")
-        : FlexOutputBuffers(componentName, name) { }
-
-    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
-        return new DummyContainerBuffer(mFormat, buffer);
-    }
-
-    sp<Codec2Buffer> allocateArrayBuffer() override {
-        return new DummyContainerBuffer(mFormat);
-    }
-};
-
-class RawGraphicOutputBuffers : public FlexOutputBuffers {
-public:
-    RawGraphicOutputBuffers(
-            size_t numOutputSlots, const char *componentName, const char *name = "2D-BB-Output")
-        : FlexOutputBuffers(componentName, name),
-          mLocalBufferPool(LocalBufferPool::Create(
-                  kMaxLinearBufferSize * numOutputSlots)) { }
-    ~RawGraphicOutputBuffers() override = default;
-
-    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
-        if (buffer == nullptr) {
-            sp<Codec2Buffer> c2buffer = ConstGraphicBlockBuffer::AllocateEmpty(
-                    mFormat,
-                    [lbp = mLocalBufferPool](size_t capacity) {
-                        return lbp->newBuffer(capacity);
-                    });
-            if (c2buffer == nullptr) {
-                ALOGD("[%s] ConstGraphicBlockBuffer::AllocateEmpty failed", mName);
-                return nullptr;
-            }
-            c2buffer->setRange(0, 0);
-            return c2buffer;
-        } else {
-            return ConstGraphicBlockBuffer::Allocate(
-                    mFormat,
-                    buffer,
-                    [lbp = mLocalBufferPool](size_t capacity) {
-                        return lbp->newBuffer(capacity);
-                    });
-        }
-    }
-
-    sp<Codec2Buffer> allocateArrayBuffer() override {
-        return ConstGraphicBlockBuffer::AllocateEmpty(
-                mFormat,
-                [lbp = mLocalBufferPool](size_t capacity) {
-                    return lbp->newBuffer(capacity);
-                });
-    }
-
-private:
-    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
-};
+constexpr size_t kSmoothnessFactor = 4;
+constexpr size_t kRenderingDepth = 3;
 
 }  // namespace
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 1ea29b4..b7a8445 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -33,6 +33,7 @@
 #include <media/stagefright/CodecBase.h>
 #include <media/ICrypto.h>
 
+#include "CCodecBuffers.h"
 #include "InputSurfaceWrapper.h"
 #include "PipelineWatcher.h"
 
@@ -151,11 +152,6 @@
 
     void setMetaMode(MetaMode mode);
 
-    // Internal classes
-    class Buffers;
-    class InputBuffers;
-    class OutputBuffers;
-
 private:
     class QueueGuard;
 
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
new file mode 100644
index 0000000..fb0efce
--- /dev/null
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -0,0 +1,963 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CCodecBuffers"
+#include <utils/Log.h>
+
+#include <C2PlatformSupport.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+#include "CCodecBuffers.h"
+
+namespace android {
+
+namespace {
+
+sp<GraphicBlockBuffer> AllocateGraphicBuffer(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const sp<AMessage> &format,
+        uint32_t pixelFormat,
+        const C2MemoryUsage &usage,
+        const std::shared_ptr<LocalBufferPool> &localBufferPool) {
+    int32_t width, height;
+    if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) {
+        ALOGD("format lacks width or height");
+        return nullptr;
+    }
+
+    std::shared_ptr<C2GraphicBlock> block;
+    c2_status_t err = pool->fetchGraphicBlock(
+            width, height, pixelFormat, usage, &block);
+    if (err != C2_OK) {
+        ALOGD("fetch graphic block failed: %d", err);
+        return nullptr;
+    }
+
+    return GraphicBlockBuffer::Allocate(
+            format,
+            block,
+            [localBufferPool](size_t capacity) {
+                return localBufferPool->newBuffer(capacity);
+            });
+}
+
+}  // namespace
+
+// CCodecBuffers
+
+void CCodecBuffers::setFormat(const sp<AMessage> &format) {
+    CHECK(format != nullptr);
+    mFormat = format;
+}
+
+sp<AMessage> CCodecBuffers::dupFormat() {
+    return mFormat != nullptr ? mFormat->dup() : nullptr;
+}
+
+void CCodecBuffers::handleImageData(const sp<Codec2Buffer> &buffer) {
+    sp<ABuffer> imageDataCandidate = buffer->getImageData();
+    if (imageDataCandidate == nullptr) {
+        return;
+    }
+    sp<ABuffer> imageData;
+    if (!mFormat->findBuffer("image-data", &imageData)
+            || imageDataCandidate->size() != imageData->size()
+            || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) {
+        ALOGD("[%s] updating image-data", mName);
+        sp<AMessage> newFormat = dupFormat();
+        newFormat->setBuffer("image-data", imageDataCandidate);
+        MediaImage2 *img = (MediaImage2*)imageDataCandidate->data();
+        if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) {
+            int32_t stride = img->mPlane[0].mRowInc;
+            newFormat->setInt32(KEY_STRIDE, stride);
+            ALOGD("[%s] updating stride = %d", mName, stride);
+            if (img->mNumPlanes > 1 && stride > 0) {
+                int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride;
+                newFormat->setInt32(KEY_SLICE_HEIGHT, vstride);
+                ALOGD("[%s] updating vstride = %d", mName, vstride);
+            }
+        }
+        setFormat(newFormat);
+        buffer->setFormat(newFormat);
+    }
+}
+
+// OutputBuffers
+
+void OutputBuffers::initSkipCutBuffer(
+        int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount) {
+    CHECK(mSkipCutBuffer == nullptr);
+    mDelay = delay;
+    mPadding = padding;
+    mSampleRate = sampleRate;
+    setSkipCutBuffer(delay, padding, channelCount);
+}
+
+void OutputBuffers::updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount) {
+    if (mSkipCutBuffer == nullptr) {
+        return;
+    }
+    int32_t delay = mDelay;
+    int32_t padding = mPadding;
+    if (sampleRate != mSampleRate) {
+        delay = ((int64_t)delay * sampleRate) / mSampleRate;
+        padding = ((int64_t)padding * sampleRate) / mSampleRate;
+    }
+    setSkipCutBuffer(delay, padding, channelCount);
+}
+
+void OutputBuffers::submit(const sp<MediaCodecBuffer> &buffer) {
+    if (mSkipCutBuffer != nullptr) {
+        mSkipCutBuffer->submit(buffer);
+    }
+}
+
+void OutputBuffers::transferSkipCutBuffer(const sp<SkipCutBuffer> &scb) {
+    mSkipCutBuffer = scb;
+}
+
+void OutputBuffers::setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount) {
+    if (mSkipCutBuffer != nullptr) {
+        size_t prevSize = mSkipCutBuffer->size();
+        if (prevSize != 0u) {
+            ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize);
+        }
+    }
+    mSkipCutBuffer = new SkipCutBuffer(skip, cut, channelCount);
+}
+
+// LocalBufferPool
+
+std::shared_ptr<LocalBufferPool> LocalBufferPool::Create(size_t poolCapacity) {
+    return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(poolCapacity));
+}
+
+sp<ABuffer> LocalBufferPool::newBuffer(size_t capacity) {
+    Mutex::Autolock lock(mMutex);
+    auto it = std::find_if(
+            mPool.begin(), mPool.end(),
+            [capacity](const std::vector<uint8_t> &vec) {
+                return vec.capacity() >= capacity;
+            });
+    if (it != mPool.end()) {
+        sp<ABuffer> buffer = new VectorBuffer(std::move(*it), shared_from_this());
+        mPool.erase(it);
+        return buffer;
+    }
+    if (mUsedSize + capacity > mPoolCapacity) {
+        while (!mPool.empty()) {
+            mUsedSize -= mPool.back().capacity();
+            mPool.pop_back();
+        }
+        if (mUsedSize + capacity > mPoolCapacity) {
+            ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
+                    mUsedSize, capacity, mPoolCapacity);
+            return nullptr;
+        }
+    }
+    std::vector<uint8_t> vec(capacity);
+    mUsedSize += vec.capacity();
+    return new VectorBuffer(std::move(vec), shared_from_this());
+}
+
+LocalBufferPool::VectorBuffer::VectorBuffer(
+        std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool)
+    : ABuffer(vec.data(), vec.capacity()),
+      mVec(std::move(vec)),
+      mPool(pool) {
+}
+
+LocalBufferPool::VectorBuffer::~VectorBuffer() {
+    std::shared_ptr<LocalBufferPool> pool = mPool.lock();
+    if (pool) {
+        // If pool is alive, return the vector back to the pool so that
+        // it can be recycled.
+        pool->returnVector(std::move(mVec));
+    }
+}
+
+void LocalBufferPool::returnVector(std::vector<uint8_t> &&vec) {
+    Mutex::Autolock lock(mMutex);
+    mPool.push_front(std::move(vec));
+}
+
+size_t FlexBuffersImpl::assignSlot(const sp<Codec2Buffer> &buffer) {
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        if (mBuffers[i].clientBuffer == nullptr
+                && mBuffers[i].compBuffer.expired()) {
+            mBuffers[i].clientBuffer = buffer;
+            return i;
+        }
+    }
+    mBuffers.push_back({ buffer, std::weak_ptr<C2Buffer>() });
+    return mBuffers.size() - 1;
+}
+
+// FlexBuffersImpl
+
+bool FlexBuffersImpl::releaseSlot(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    sp<Codec2Buffer> clientBuffer;
+    size_t index = mBuffers.size();
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        if (mBuffers[i].clientBuffer == buffer) {
+            clientBuffer = mBuffers[i].clientBuffer;
+            if (release) {
+                mBuffers[i].clientBuffer.clear();
+            }
+            index = i;
+            break;
+        }
+    }
+    if (clientBuffer == nullptr) {
+        ALOGV("[%s] %s: No matching buffer found", mName, __func__);
+        return false;
+    }
+    std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
+    if (!result) {
+        result = clientBuffer->asC2Buffer();
+        mBuffers[index].compBuffer = result;
+    }
+    if (c2buffer) {
+        *c2buffer = result;
+    }
+    return true;
+}
+
+bool FlexBuffersImpl::expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        std::shared_ptr<C2Buffer> compBuffer =
+                mBuffers[i].compBuffer.lock();
+        if (!compBuffer || compBuffer != c2buffer) {
+            continue;
+        }
+        mBuffers[i].compBuffer.reset();
+        ALOGV("[%s] codec released buffer #%zu", mName, i);
+        return true;
+    }
+    ALOGV("[%s] codec released an unknown buffer", mName);
+    return false;
+}
+
+void FlexBuffersImpl::flush() {
+    ALOGV("[%s] buffers are flushed %zu", mName, mBuffers.size());
+    mBuffers.clear();
+}
+
+size_t FlexBuffersImpl::numClientBuffers() const {
+    return std::count_if(
+            mBuffers.begin(), mBuffers.end(),
+            [](const Entry &entry) {
+                return (entry.clientBuffer != nullptr);
+            });
+}
+
+// BuffersArrayImpl
+
+void BuffersArrayImpl::initialize(
+        const FlexBuffersImpl &impl,
+        size_t minSize,
+        std::function<sp<Codec2Buffer>()> allocate) {
+    mImplName = impl.mImplName + "[N]";
+    mName = mImplName.c_str();
+    for (size_t i = 0; i < impl.mBuffers.size(); ++i) {
+        sp<Codec2Buffer> clientBuffer = impl.mBuffers[i].clientBuffer;
+        bool ownedByClient = (clientBuffer != nullptr);
+        if (!ownedByClient) {
+            clientBuffer = allocate();
+        }
+        mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient });
+    }
+    ALOGV("[%s] converted %zu buffers to array mode of %zu", mName, mBuffers.size(), minSize);
+    for (size_t i = impl.mBuffers.size(); i < minSize; ++i) {
+        mBuffers.push_back({ allocate(), std::weak_ptr<C2Buffer>(), false });
+    }
+}
+
+status_t BuffersArrayImpl::grabBuffer(
+        size_t *index,
+        sp<Codec2Buffer> *buffer,
+        std::function<bool(const sp<Codec2Buffer> &)> match) {
+    // 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;
+        }
+    }
+    return allBuffersDontMatch ? NO_MEMORY : WOULD_BLOCK;
+}
+
+bool BuffersArrayImpl::returnBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    sp<Codec2Buffer> clientBuffer;
+    size_t index = mBuffers.size();
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        if (mBuffers[i].clientBuffer == buffer) {
+            if (!mBuffers[i].ownedByClient) {
+                ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu",
+                      mName, i);
+            }
+            clientBuffer = mBuffers[i].clientBuffer;
+            if (release) {
+                mBuffers[i].ownedByClient = false;
+            }
+            index = i;
+            break;
+        }
+    }
+    if (clientBuffer == nullptr) {
+        ALOGV("[%s] %s: No matching buffer found", mName, __func__);
+        return false;
+    }
+    ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index);
+    std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
+    if (!result) {
+        result = clientBuffer->asC2Buffer();
+        mBuffers[index].compBuffer = result;
+    }
+    if (c2buffer) {
+        *c2buffer = result;
+    }
+    return true;
+}
+
+bool BuffersArrayImpl::expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        std::shared_ptr<C2Buffer> compBuffer =
+                mBuffers[i].compBuffer.lock();
+        if (!compBuffer) {
+            continue;
+        }
+        if (c2buffer == compBuffer) {
+            if (mBuffers[i].ownedByClient) {
+                // This should not happen.
+                ALOGD("[%s] codec released a buffer owned by client "
+                      "(index %zu)", mName, i);
+            }
+            mBuffers[i].compBuffer.reset();
+            ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i);
+            return true;
+        }
+    }
+    ALOGV("[%s] codec released an unknown buffer (array mode)", mName);
+    return false;
+}
+
+void BuffersArrayImpl::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+    array->clear();
+    for (const Entry &entry : mBuffers) {
+        array->push(entry.clientBuffer);
+    }
+}
+
+void BuffersArrayImpl::flush() {
+    for (Entry &entry : mBuffers) {
+        entry.ownedByClient = false;
+    }
+}
+
+void BuffersArrayImpl::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 });
+    }
+}
+
+size_t BuffersArrayImpl::numClientBuffers() const {
+    return std::count_if(
+            mBuffers.begin(), mBuffers.end(),
+            [](const Entry &entry) {
+                return entry.ownedByClient;
+            });
+}
+
+// InputBuffersArray
+
+void InputBuffersArray::initialize(
+        const FlexBuffersImpl &impl,
+        size_t minSize,
+        std::function<sp<Codec2Buffer>()> allocate) {
+    mImpl.initialize(impl, minSize, allocate);
+}
+
+void InputBuffersArray::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+    mImpl.getArray(array);
+}
+
+bool InputBuffersArray::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
+    sp<Codec2Buffer> c2Buffer;
+    status_t err = mImpl.grabBuffer(index, &c2Buffer);
+    if (err == OK) {
+        c2Buffer->setFormat(mFormat);
+        handleImageData(c2Buffer);
+        *buffer = c2Buffer;
+        return true;
+    }
+    return false;
+}
+
+bool InputBuffersArray::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    return mImpl.returnBuffer(buffer, c2buffer, release);
+}
+
+bool InputBuffersArray::expireComponentBuffer(
+        const std::shared_ptr<C2Buffer> &c2buffer) {
+    return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void InputBuffersArray::flush() {
+    mImpl.flush();
+}
+
+size_t InputBuffersArray::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// 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);
+    if (newBuffer == nullptr) {
+        return false;
+    }
+    *index = mImpl.assignSlot(newBuffer);
+    *buffer = newBuffer;
+    return true;
+}
+
+bool LinearInputBuffers::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    return mImpl.releaseSlot(buffer, c2buffer, release);
+}
+
+bool LinearInputBuffers::expireComponentBuffer(
+        const std::shared_ptr<C2Buffer> &c2buffer) {
+    return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void LinearInputBuffers::flush() {
+    // This is no-op by default unless we're in array mode where we need to keep
+    // track of the flushed work.
+    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<InputBuffersArray> array(
+            new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]"));
+    array->setPool(mPool);
+    array->setFormat(mFormat);
+    array->initialize(
+            mImpl,
+            size,
+            [this, capacity] () -> sp<Codec2Buffer> { return alloc(capacity); });
+    return std::move(array);
+}
+
+size_t LinearInputBuffers::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+sp<Codec2Buffer> LinearInputBuffers::alloc(size_t size) {
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    std::shared_ptr<C2LinearBlock> block;
+
+    c2_status_t err = mPool->fetchLinearBlock(size, usage, &block);
+    if (err != C2_OK) {
+        return nullptr;
+    }
+
+    return LinearBlockBuffer::Allocate(mFormat, block);
+}
+
+// EncryptedLinearInputBuffers
+
+EncryptedLinearInputBuffers::EncryptedLinearInputBuffers(
+        bool secure,
+        const sp<MemoryDealer> &dealer,
+        const sp<ICrypto> &crypto,
+        int32_t heapSeqNum,
+        size_t capacity,
+        size_t numInputSlots,
+        const char *componentName, const char *name)
+    : LinearInputBuffers(componentName, name),
+      mUsage({0, 0}),
+      mDealer(dealer),
+      mCrypto(crypto),
+      mHeapSeqNum(heapSeqNum) {
+    if (secure) {
+        mUsage = { C2MemoryUsage::READ_PROTECTED, 0 };
+    } else {
+        mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    }
+    for (size_t i = 0; i < numInputSlots; ++i) {
+        sp<IMemory> memory = mDealer->allocate(capacity);
+        if (memory == nullptr) {
+            ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated",
+                  mName, i);
+            break;
+        }
+        mMemoryVector.push_back({std::weak_ptr<C2LinearBlock>(), memory});
+    }
+}
+
+sp<Codec2Buffer> EncryptedLinearInputBuffers::alloc(size_t size) {
+    sp<IMemory> memory;
+    size_t slot = 0;
+    for (; slot < mMemoryVector.size(); ++slot) {
+        if (mMemoryVector[slot].block.expired()) {
+            memory = mMemoryVector[slot].memory;
+            break;
+        }
+    }
+    if (memory == nullptr) {
+        return nullptr;
+    }
+
+    std::shared_ptr<C2LinearBlock> block;
+    c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block);
+    if (err != C2_OK || block == nullptr) {
+        return nullptr;
+    }
+
+    mMemoryVector[slot].block = block;
+    return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum);
+}
+
+// GraphicMetadataInputBuffers
+
+GraphicMetadataInputBuffers::GraphicMetadataInputBuffers(
+        const char *componentName, const char *name)
+    : InputBuffers(componentName, name),
+      mImpl(mName),
+      mStore(GetCodec2PlatformAllocatorStore()) { }
+
+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);
+    if (newBuffer == nullptr) {
+        return false;
+    }
+    *index = mImpl.assignSlot(newBuffer);
+    *buffer = newBuffer;
+    return true;
+}
+
+bool GraphicMetadataInputBuffers::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    return mImpl.releaseSlot(buffer, c2buffer, release);
+}
+
+bool GraphicMetadataInputBuffers::expireComponentBuffer(
+        const std::shared_ptr<C2Buffer> &c2buffer) {
+    return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void GraphicMetadataInputBuffers::flush() {
+    // This is no-op by default unless we're in array mode where we need to keep
+    // track of the flushed work.
+}
+
+std::unique_ptr<InputBuffers> GraphicMetadataInputBuffers::toArrayMode(
+        size_t size) {
+    std::shared_ptr<C2Allocator> alloc;
+    c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
+    if (err != C2_OK) {
+        return nullptr;
+    }
+    std::unique_ptr<InputBuffersArray> array(
+            new InputBuffersArray(mComponentName.c_str(), "2D-MetaInput[N]"));
+    array->setPool(mPool);
+    array->setFormat(mFormat);
+    array->initialize(
+            mImpl,
+            size,
+            [format = mFormat, alloc]() -> sp<Codec2Buffer> {
+                return new GraphicMetadataBuffer(format, alloc);
+            });
+    return std::move(array);
+}
+
+size_t GraphicMetadataInputBuffers::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// GraphicInputBuffers
+
+GraphicInputBuffers::GraphicInputBuffers(
+        size_t numInputSlots, const char *componentName, const char *name)
+    : InputBuffers(componentName, name),
+      mImpl(mName),
+      mLocalBufferPool(LocalBufferPool::Create(
+              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);
+    if (newBuffer == nullptr) {
+        return false;
+    }
+    *index = mImpl.assignSlot(newBuffer);
+    handleImageData(newBuffer);
+    *buffer = newBuffer;
+    return true;
+}
+
+bool GraphicInputBuffers::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    return mImpl.releaseSlot(buffer, c2buffer, release);
+}
+
+bool GraphicInputBuffers::expireComponentBuffer(
+        const std::shared_ptr<C2Buffer> &c2buffer) {
+    return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void GraphicInputBuffers::flush() {
+    // This is no-op by default unless we're in array mode where we need to keep
+    // track of the flushed work.
+}
+
+std::unique_ptr<InputBuffers> GraphicInputBuffers::toArrayMode(size_t size) {
+    std::unique_ptr<InputBuffersArray> array(
+            new InputBuffersArray(mComponentName.c_str(), "2D-BB-Input[N]"));
+    array->setPool(mPool);
+    array->setFormat(mFormat);
+    array->initialize(
+            mImpl,
+            size,
+            [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp<Codec2Buffer> {
+                C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+                return AllocateGraphicBuffer(
+                        pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp);
+            });
+    return std::move(array);
+}
+
+size_t GraphicInputBuffers::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// OutputBuffersArray
+
+void OutputBuffersArray::initialize(
+        const FlexBuffersImpl &impl,
+        size_t minSize,
+        std::function<sp<Codec2Buffer>()> allocate) {
+    mImpl.initialize(impl, minSize, allocate);
+}
+
+status_t OutputBuffersArray::registerBuffer(
+        const std::shared_ptr<C2Buffer> &buffer,
+        size_t *index,
+        sp<MediaCodecBuffer> *clientBuffer) {
+    sp<Codec2Buffer> c2Buffer;
+    status_t err = mImpl.grabBuffer(
+            index,
+            &c2Buffer,
+            [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) {
+        ALOGD("[%s] grabBuffer failed: %d", mName, err);
+        return err;
+    }
+    c2Buffer->setFormat(mFormat);
+    if (!c2Buffer->copy(buffer)) {
+        ALOGD("[%s] copy buffer failed", mName);
+        return WOULD_BLOCK;
+    }
+    submit(c2Buffer);
+    handleImageData(c2Buffer);
+    *clientBuffer = c2Buffer;
+    ALOGV("[%s] grabbed buffer %zu", mName, *index);
+    return OK;
+}
+
+status_t OutputBuffersArray::registerCsd(
+        const C2StreamInitDataInfo::output *csd,
+        size_t *index,
+        sp<MediaCodecBuffer> *clientBuffer) {
+    sp<Codec2Buffer> c2Buffer;
+    status_t err = mImpl.grabBuffer(
+            index,
+            &c2Buffer,
+            [csd](const sp<Codec2Buffer> &clientBuffer) {
+                return clientBuffer->base() != nullptr
+                        && clientBuffer->capacity() >= csd->flexCount();
+            });
+    if (err != OK) {
+        return err;
+    }
+    memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
+    c2Buffer->setRange(0, csd->flexCount());
+    c2Buffer->setFormat(mFormat);
+    *clientBuffer = c2Buffer;
+    return OK;
+}
+
+bool OutputBuffersArray::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) {
+    return mImpl.returnBuffer(buffer, c2buffer, true);
+}
+
+void OutputBuffersArray::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
+    (void)flushedWork;
+    mImpl.flush();
+    if (mSkipCutBuffer != nullptr) {
+        mSkipCutBuffer->clear();
+    }
+}
+
+void OutputBuffersArray::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+    mImpl.getArray(array);
+}
+
+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;
+            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]];
+
+        case C2BufferData::INVALID:         [[fallthrough]];
+        case C2BufferData::LINEAR_CHUNKS:   [[fallthrough]];
+        case C2BufferData::GRAPHIC_CHUNKS:  [[fallthrough]];
+        default:
+            ALOGD("Unsupported type: %d", (int)c2buffer->data().type());
+            return;
+    }
+    mImpl.realloc(alloc);
+}
+
+size_t OutputBuffersArray::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// FlexOutputBuffers
+
+status_t FlexOutputBuffers::registerBuffer(
+        const std::shared_ptr<C2Buffer> &buffer,
+        size_t *index,
+        sp<MediaCodecBuffer> *clientBuffer) {
+    sp<Codec2Buffer> newBuffer = wrap(buffer);
+    if (newBuffer == nullptr) {
+        return NO_MEMORY;
+    }
+    newBuffer->setFormat(mFormat);
+    *index = mImpl.assignSlot(newBuffer);
+    handleImageData(newBuffer);
+    *clientBuffer = newBuffer;
+    ALOGV("[%s] registered buffer %zu", mName, *index);
+    return OK;
+}
+
+status_t FlexOutputBuffers::registerCsd(
+        const C2StreamInitDataInfo::output *csd,
+        size_t *index,
+        sp<MediaCodecBuffer> *clientBuffer) {
+    sp<Codec2Buffer> newBuffer = new LocalLinearBuffer(
+            mFormat, ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
+    *index = mImpl.assignSlot(newBuffer);
+    *clientBuffer = newBuffer;
+    return OK;
+}
+
+bool FlexOutputBuffers::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer) {
+    return mImpl.releaseSlot(buffer, c2buffer, true);
+}
+
+void FlexOutputBuffers::flush(
+        const std::list<std::unique_ptr<C2Work>> &flushedWork) {
+    (void) flushedWork;
+    // This is no-op by default unless we're in array mode where we need to keep
+    // track of the flushed work.
+}
+
+std::unique_ptr<OutputBuffers> FlexOutputBuffers::toArrayMode(size_t size) {
+    std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray(mComponentName.c_str()));
+    array->setFormat(mFormat);
+    array->transferSkipCutBuffer(mSkipCutBuffer);
+    array->initialize(
+            mImpl,
+            size,
+            [this]() { return allocateArrayBuffer(); });
+    return std::move(array);
+}
+
+size_t FlexOutputBuffers::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// LinearOutputBuffers
+
+void LinearOutputBuffers::flush(
+        const std::list<std::unique_ptr<C2Work>> &flushedWork) {
+    if (mSkipCutBuffer != nullptr) {
+        mSkipCutBuffer->clear();
+    }
+    FlexOutputBuffers::flush(flushedWork);
+}
+
+sp<Codec2Buffer> LinearOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
+    if (buffer == nullptr) {
+        ALOGV("[%s] using a dummy buffer", mName);
+        return new LocalLinearBuffer(mFormat, new ABuffer(0));
+    }
+    if (buffer->data().type() != C2BufferData::LINEAR) {
+        ALOGV("[%s] non-linear buffer %d", mName, buffer->data().type());
+        // We expect linear output buffers from the component.
+        return nullptr;
+    }
+    if (buffer->data().linearBlocks().size() != 1u) {
+        ALOGV("[%s] no linear buffers", mName);
+        // We expect one and only one linear block from the component.
+        return nullptr;
+    }
+    sp<Codec2Buffer> clientBuffer = ConstLinearBlockBuffer::Allocate(mFormat, buffer);
+    if (clientBuffer == nullptr) {
+        ALOGD("[%s] ConstLinearBlockBuffer::Allocate failed", mName);
+        return nullptr;
+    }
+    submit(clientBuffer);
+    return clientBuffer;
+}
+
+sp<Codec2Buffer> LinearOutputBuffers::allocateArrayBuffer() {
+    // TODO: proper max output size
+    return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
+}
+
+// GraphicOutputBuffers
+
+sp<Codec2Buffer> GraphicOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
+    return new DummyContainerBuffer(mFormat, buffer);
+}
+
+sp<Codec2Buffer> GraphicOutputBuffers::allocateArrayBuffer() {
+    return new DummyContainerBuffer(mFormat);
+}
+
+// RawGraphicOutputBuffers
+
+RawGraphicOutputBuffers::RawGraphicOutputBuffers(
+        size_t numOutputSlots, const char *componentName, const char *name)
+    : FlexOutputBuffers(componentName, name),
+      mLocalBufferPool(LocalBufferPool::Create(
+              kMaxLinearBufferSize * numOutputSlots)) { }
+
+sp<Codec2Buffer> RawGraphicOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
+    if (buffer == nullptr) {
+        sp<Codec2Buffer> c2buffer = ConstGraphicBlockBuffer::AllocateEmpty(
+                mFormat,
+                [lbp = mLocalBufferPool](size_t capacity) {
+                    return lbp->newBuffer(capacity);
+                });
+        if (c2buffer == nullptr) {
+            ALOGD("[%s] ConstGraphicBlockBuffer::AllocateEmpty failed", mName);
+            return nullptr;
+        }
+        c2buffer->setRange(0, 0);
+        return c2buffer;
+    } else {
+        return ConstGraphicBlockBuffer::Allocate(
+                mFormat,
+                buffer,
+                [lbp = mLocalBufferPool](size_t capacity) {
+                    return lbp->newBuffer(capacity);
+                });
+    }
+}
+
+sp<Codec2Buffer> RawGraphicOutputBuffers::allocateArrayBuffer() {
+    return ConstGraphicBlockBuffer::AllocateEmpty(
+            mFormat,
+            [lbp = mLocalBufferPool](size_t capacity) {
+                return lbp->newBuffer(capacity);
+            });
+}
+
+}  // namespace android
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
new file mode 100644
index 0000000..f5d9fee
--- /dev/null
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -0,0 +1,808 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CCODEC_BUFFERS_H_
+
+#define CCODEC_BUFFERS_H_
+
+#include <string>
+
+#include <C2Config.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/MediaCodecBuffer.h>
+
+#include "Codec2Buffer.h"
+#include "SkipCutBuffer.h"
+
+namespace android {
+
+constexpr size_t kLinearBufferSize = 1048576;
+// This can fit 4K RGBA frame, and most likely client won't need more than this.
+constexpr size_t kMaxLinearBufferSize = 3840 * 2160 * 4;
+
+/**
+ * Base class for representation of buffers at one port.
+ */
+class CCodecBuffers {
+public:
+    CCodecBuffers(const char *componentName, const char *name = "Buffers")
+        : mComponentName(componentName),
+          mChannelName(std::string(componentName) + ":" + name),
+          mName(mChannelName.c_str()) {
+    }
+    virtual ~CCodecBuffers() = default;
+
+    /**
+     * Set format for MediaCodec-facing buffers.
+     */
+    void setFormat(const sp<AMessage> &format);
+
+    /**
+     * Return a copy of current format.
+     */
+    sp<AMessage> dupFormat();
+
+    /**
+     * Returns true if the buffers are operating under array mode.
+     */
+    virtual bool isArrayMode() const { return false; }
+
+    /**
+     * Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
+     * no-op.
+     */
+    virtual void getArray(Vector<sp<MediaCodecBuffer>> *) const {}
+
+    /**
+     * Return number of buffers the client owns.
+     */
+    virtual size_t numClientBuffers() const = 0;
+
+    /**
+     * Examine image data from the buffer and update the format if necessary.
+     */
+    void handleImageData(const sp<Codec2Buffer> &buffer);
+
+protected:
+    std::string mComponentName; ///< name of component for debugging
+    std::string mChannelName; ///< name of channel for debugging
+    const char *mName; ///< C-string version of channel name
+    // Format to be used for creating MediaCodec-facing buffers.
+    sp<AMessage> mFormat;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(CCodecBuffers);
+};
+
+class InputBuffers : public CCodecBuffers {
+public:
+    InputBuffers(const char *componentName, const char *name = "Input[]")
+        : CCodecBuffers(componentName, name) { }
+    virtual ~InputBuffers() = default;
+
+    /**
+     * Set a block pool to obtain input memory blocks.
+     */
+    void setPool(const std::shared_ptr<C2BlockPool> &pool) { mPool = pool; }
+
+    /**
+     * Get a new MediaCodecBuffer for input and its corresponding index.
+     * Returns false if no new buffer can be obtained at the moment.
+     */
+    virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
+
+    /**
+     * Release the buffer obtained from requestNewBuffer() and get the
+     * associated C2Buffer object back. Returns true if the buffer was on file
+     * and released successfully.
+     */
+    virtual bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) = 0;
+
+    /**
+     * Release the buffer that is no longer used by the codec process. Return
+     * true if and only if the buffer was on file and released successfully.
+     */
+    virtual bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) = 0;
+
+    /**
+     * Flush internal state. After this call, no index or buffer previously
+     * returned from requestNewBuffer() is valid.
+     */
+    virtual void flush() = 0;
+
+    /**
+     * Return array-backed version of input buffers. The returned object
+     * shall retain the internal state so that it will honor index and
+     * buffer from previous calls of requestNewBuffer().
+     */
+    virtual std::unique_ptr<InputBuffers> toArrayMode(size_t size) = 0;
+
+protected:
+    // Pool to obtain blocks for input buffers.
+    std::shared_ptr<C2BlockPool> mPool;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
+};
+
+class OutputBuffers : public CCodecBuffers {
+public:
+    OutputBuffers(const char *componentName, const char *name = "Output")
+        : CCodecBuffers(componentName, name) { }
+    virtual ~OutputBuffers() = default;
+
+    /**
+     * Register output C2Buffer from the component and obtain corresponding
+     * index and MediaCodecBuffer object. Returns false if registration
+     * fails.
+     */
+    virtual status_t registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) = 0;
+
+    /**
+     * Register codec specific data as a buffer to be consistent with
+     * MediaCodec behavior.
+     */
+    virtual status_t registerCsd(
+            const C2StreamInitDataInfo::output * /* csd */,
+            size_t * /* index */,
+            sp<MediaCodecBuffer> * /* clientBuffer */) = 0;
+
+    /**
+     * Release the buffer obtained from registerBuffer() and get the
+     * associated C2Buffer object back. Returns true if the buffer was on file
+     * and released successfully.
+     */
+    virtual bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) = 0;
+
+    /**
+     * Flush internal state. After this call, no index or buffer previously
+     * returned from registerBuffer() is valid.
+     */
+    virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) = 0;
+
+    /**
+     * Return array-backed version of output buffers. The returned object
+     * shall retain the internal state so that it will honor index and
+     * buffer from previous calls of registerBuffer().
+     */
+    virtual std::unique_ptr<OutputBuffers> toArrayMode(size_t size) = 0;
+
+    /**
+     * Initialize SkipCutBuffer object.
+     */
+    void initSkipCutBuffer(
+            int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount);
+
+    /**
+     * Update the SkipCutBuffer object. No-op if it's never initialized.
+     */
+    void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount);
+
+    /**
+     * Submit buffer to SkipCutBuffer object, if initialized.
+     */
+    void submit(const sp<MediaCodecBuffer> &buffer);
+
+    /**
+     * Transfer SkipCutBuffer object to the other Buffers object.
+     */
+    void transferSkipCutBuffer(const sp<SkipCutBuffer> &scb);
+
+protected:
+    sp<SkipCutBuffer> mSkipCutBuffer;
+
+private:
+    int32_t mDelay;
+    int32_t mPadding;
+    int32_t mSampleRate;
+
+    void setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount);
+
+    DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
+};
+
+/**
+ * Simple local buffer pool backed by std::vector.
+ */
+class LocalBufferPool : public std::enable_shared_from_this<LocalBufferPool> {
+public:
+    /**
+     * Create a new LocalBufferPool object.
+     *
+     * \param poolCapacity  max total size of buffers managed by this pool.
+     *
+     * \return  a newly created pool object.
+     */
+    static std::shared_ptr<LocalBufferPool> Create(size_t poolCapacity);
+
+    /**
+     * Return an ABuffer object whose size is at least |capacity|.
+     *
+     * \param   capacity  requested capacity
+     * \return  nullptr if the pool capacity is reached
+     *          an ABuffer object otherwise.
+     */
+    sp<ABuffer> newBuffer(size_t capacity);
+
+private:
+    /**
+     * ABuffer backed by std::vector.
+     */
+    class VectorBuffer : public ::android::ABuffer {
+    public:
+        /**
+         * Construct a VectorBuffer by taking the ownership of supplied vector.
+         *
+         * \param vec   backing vector of the buffer. this object takes
+         *              ownership at construction.
+         * \param pool  a LocalBufferPool object to return the vector at
+         *              destruction.
+         */
+        VectorBuffer(std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool);
+
+        ~VectorBuffer() override;
+
+    private:
+        std::vector<uint8_t> mVec;
+        std::weak_ptr<LocalBufferPool> mPool;
+    };
+
+    Mutex mMutex;
+    size_t mPoolCapacity;
+    size_t mUsedSize;
+    std::list<std::vector<uint8_t>> mPool;
+
+    /**
+     * Private constructor to prevent constructing non-managed LocalBufferPool.
+     */
+    explicit LocalBufferPool(size_t poolCapacity)
+        : mPoolCapacity(poolCapacity), mUsedSize(0) {
+    }
+
+    /**
+     * Take back the ownership of vec from the destructed VectorBuffer and put
+     * it in front of the pool.
+     */
+    void returnVector(std::vector<uint8_t> &&vec);
+
+    DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool);
+};
+
+class BuffersArrayImpl;
+
+/**
+ * Flexible buffer slots implementation.
+ */
+class FlexBuffersImpl {
+public:
+    FlexBuffersImpl(const char *name)
+        : mImplName(std::string(name) + ".Impl"),
+          mName(mImplName.c_str()) { }
+
+    /**
+     * Assign an empty slot for a buffer and return the index. If there's no
+     * empty slot, just add one at the end and return it.
+     *
+     * \param buffer[in]  a new buffer to assign a slot.
+     * \return            index of the assigned slot.
+     */
+    size_t assignSlot(const sp<Codec2Buffer> &buffer);
+
+    /**
+     * Release the slot from the client, and get the C2Buffer object back from
+     * the previously assigned buffer. Note that the slot is not completely free
+     * until the returned C2Buffer object is freed.
+     *
+     * \param   buffer[in]        the buffer previously assigned a slot.
+     * \param   c2buffer[in,out]  pointer to C2Buffer to be populated. Ignored
+     *                            if null.
+     * \return  true  if the buffer is successfully released from a slot
+     *          false otherwise
+     */
+    bool releaseSlot(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release);
+
+    /**
+     * Expire the C2Buffer object in the slot.
+     *
+     * \param   c2buffer[in]  C2Buffer object which the component released.
+     * \return  true  if the buffer is found in one of the slots and
+     *                successfully released
+     *          false otherwise
+     */
+    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer);
+
+    /**
+     * The client abandoned all known buffers, so reclaim the ownership.
+     */
+    void flush();
+
+    /**
+     * Return the number of buffers that are sent to the client but not released
+     * yet.
+     */
+    size_t numClientBuffers() const;
+
+private:
+    friend class BuffersArrayImpl;
+
+    std::string mImplName; ///< name for debugging
+    const char *mName; ///< C-string version of name
+
+    struct Entry {
+        sp<Codec2Buffer> clientBuffer;
+        std::weak_ptr<C2Buffer> compBuffer;
+    };
+    std::vector<Entry> mBuffers;
+};
+
+/**
+ * Static buffer slots implementation based on a fixed-size array.
+ */
+class BuffersArrayImpl {
+public:
+    BuffersArrayImpl()
+        : mImplName("BuffersArrayImpl"),
+          mName(mImplName.c_str()) { }
+
+    /**
+     * Initialize buffer array from the original |impl|. The buffers known by
+     * the client is preserved, and the empty slots are populated so that the
+     * array size is at least |minSize|.
+     *
+     * \param impl[in]      FlexBuffersImpl object used so far.
+     * \param minSize[in]   minimum size of the buffer array.
+     * \param allocate[in]  function to allocate a client buffer for an empty slot.
+     */
+    void initialize(
+            const FlexBuffersImpl &impl,
+            size_t minSize,
+            std::function<sp<Codec2Buffer>()> allocate);
+
+    /**
+     * Grab a buffer from the underlying array which matches the criteria.
+     *
+     * \param index[out]    index of the slot.
+     * \param buffer[out]   the matching buffer.
+     * \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
+     */
+    status_t grabBuffer(
+            size_t *index,
+            sp<Codec2Buffer> *buffer,
+            std::function<bool(const sp<Codec2Buffer> &)> match =
+                [](const sp<Codec2Buffer> &) { return true; });
+
+    /**
+     * Return the buffer from the client, and get the C2Buffer object back from
+     * the buffer. Note that the slot is not completely free until the returned
+     * C2Buffer object is freed.
+     *
+     * \param   buffer[in]        the buffer previously grabbed.
+     * \param   c2buffer[in,out]  pointer to C2Buffer to be populated. Ignored
+     *                            if null.
+     * \return  true  if the buffer is successfully returned
+     *          false otherwise
+     */
+    bool returnBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release);
+
+    /**
+     * Expire the C2Buffer object in the slot.
+     *
+     * \param   c2buffer[in]  C2Buffer object which the component released.
+     * \return  true  if the buffer is found in one of the slots and
+     *                successfully released
+     *          false otherwise
+     */
+    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer);
+
+    /**
+     * Populate |array| with the underlying buffer array.
+     *
+     * \param array[out]  an array to be filled with the underlying buffer array.
+     */
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const;
+
+    /**
+     * The client abandoned all known buffers, so reclaim the ownership.
+     */
+    void flush();
+
+    /**
+     * Reallocate the array with the given allocation function.
+     *
+     * \param alloc[in] the allocation function for client buffers.
+     */
+    void realloc(std::function<sp<Codec2Buffer>()> alloc);
+
+    /**
+     * Return the number of buffers that are sent to the client but not released
+     * yet.
+     */
+    size_t numClientBuffers() const;
+
+private:
+    std::string mImplName; ///< name for debugging
+    const char *mName; ///< C-string version of name
+
+    struct Entry {
+        const sp<Codec2Buffer> clientBuffer;
+        std::weak_ptr<C2Buffer> compBuffer;
+        bool ownedByClient;
+    };
+    std::vector<Entry> mBuffers;
+};
+
+class InputBuffersArray : public InputBuffers {
+public:
+    InputBuffersArray(const char *componentName, const char *name = "Input[N]")
+        : InputBuffers(componentName, name) { }
+    ~InputBuffersArray() override = default;
+
+    /**
+     * Initialize this object from the non-array state. We keep existing slots
+     * at the same index, and for empty slots we allocate client buffers with
+     * the given allocate function. If the number of slots is less than minSize,
+     * we fill the array to the minimum size.
+     *
+     * \param impl[in]      existing non-array state
+     * \param minSize[in]   minimum size of the array
+     * \param allocate[in]  allocate function to fill empty slots
+     */
+    void initialize(
+            const FlexBuffersImpl &impl,
+            size_t minSize,
+            std::function<sp<Codec2Buffer>()> allocate);
+
+    bool isArrayMode() const final { return true; }
+
+    std::unique_ptr<InputBuffers> toArrayMode(size_t) final {
+        return nullptr;
+    }
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override;
+
+    bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) override;
+
+    void flush() override;
+
+    size_t numClientBuffers() const final;
+
+private:
+    BuffersArrayImpl mImpl;
+};
+
+class LinearInputBuffers : public InputBuffers {
+public:
+    LinearInputBuffers(const char *componentName, const char *name = "1D-Input")
+        : InputBuffers(componentName, name),
+          mImpl(mName) { }
+    ~LinearInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override;
+
+    bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) override;
+
+    void flush() override;
+
+    std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
+
+    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);
+
+private:
+    FlexBuffersImpl mImpl;
+};
+
+class EncryptedLinearInputBuffers : public LinearInputBuffers {
+public:
+    EncryptedLinearInputBuffers(
+            bool secure,
+            const sp<MemoryDealer> &dealer,
+            const sp<ICrypto> &crypto,
+            int32_t heapSeqNum,
+            size_t capacity,
+            size_t numInputSlots,
+            const char *componentName, const char *name = "EncryptedInput");
+
+    ~EncryptedLinearInputBuffers() override = default;
+
+    sp<Codec2Buffer> alloc(size_t size) override;
+
+private:
+    C2MemoryUsage mUsage;
+    sp<MemoryDealer> mDealer;
+    sp<ICrypto> mCrypto;
+    int32_t mHeapSeqNum;
+    struct Entry {
+        std::weak_ptr<C2LinearBlock> block;
+        sp<IMemory> memory;
+    };
+    std::vector<Entry> mMemoryVector;
+};
+
+class GraphicMetadataInputBuffers : public InputBuffers {
+public:
+    GraphicMetadataInputBuffers(const char *componentName, const char *name = "2D-MetaInput");
+    ~GraphicMetadataInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override;
+
+    bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) override;
+
+    void flush() override;
+
+    std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
+
+    size_t numClientBuffers() const final;
+
+private:
+    FlexBuffersImpl mImpl;
+    std::shared_ptr<C2AllocatorStore> mStore;
+};
+
+class GraphicInputBuffers : public InputBuffers {
+public:
+    GraphicInputBuffers(
+            size_t numInputSlots, const char *componentName, const char *name = "2D-BB-Input");
+    ~GraphicInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override;
+
+    bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) override;
+
+    void flush() override;
+
+    std::unique_ptr<InputBuffers> toArrayMode(
+            size_t size) final;
+
+    size_t numClientBuffers() const final;
+
+private:
+    FlexBuffersImpl mImpl;
+    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
+class DummyInputBuffers : public InputBuffers {
+public:
+    DummyInputBuffers(const char *componentName, const char *name = "2D-Input")
+        : InputBuffers(componentName, name) { }
+    ~DummyInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
+        return false;
+    }
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &, std::shared_ptr<C2Buffer> *, bool) override {
+        return false;
+    }
+
+    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &) override {
+        return false;
+    }
+    void flush() override {
+    }
+
+    std::unique_ptr<InputBuffers> toArrayMode(size_t) final {
+        return nullptr;
+    }
+
+    bool isArrayMode() const final { return true; }
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
+        array->clear();
+    }
+
+    size_t numClientBuffers() const final {
+        return 0u;
+    }
+};
+
+class OutputBuffersArray : public OutputBuffers {
+public:
+    OutputBuffersArray(const char *componentName, const char *name = "Output[N]")
+        : OutputBuffers(componentName, name) { }
+    ~OutputBuffersArray() override = default;
+
+    /**
+     * Initialize this object from the non-array state. We keep existing slots
+     * at the same index, and for empty slots we allocate client buffers with
+     * the given allocate function. If the number of slots is less than minSize,
+     * we fill the array to the minimum size.
+     *
+     * \param impl[in]      existing non-array state
+     * \param minSize[in]   minimum size of the array
+     * \param allocate[in]  allocate function to fill empty slots
+     */
+    void initialize(
+            const FlexBuffersImpl &impl,
+            size_t minSize,
+            std::function<sp<Codec2Buffer>()> allocate);
+
+    bool isArrayMode() const final { return true; }
+
+    std::unique_ptr<OutputBuffers> toArrayMode(size_t) final {
+        return nullptr;
+    }
+
+    status_t registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) final;
+
+    status_t registerCsd(
+            const C2StreamInitDataInfo::output *csd,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) final;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override;
+
+    void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) override;
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final;
+
+    /**
+     * Reallocate the array, filled with buffers with the same size as given
+     * buffer.
+     *
+     * \param c2buffer[in] the reference buffer
+     */
+    void realloc(const std::shared_ptr<C2Buffer> &c2buffer);
+
+    size_t numClientBuffers() const final;
+
+private:
+    BuffersArrayImpl mImpl;
+};
+
+class FlexOutputBuffers : public OutputBuffers {
+public:
+    FlexOutputBuffers(const char *componentName, const char *name = "Output[]")
+        : OutputBuffers(componentName, name),
+          mImpl(mName) { }
+
+    status_t registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) override;
+
+    status_t registerCsd(
+            const C2StreamInitDataInfo::output *csd,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) final;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer) override;
+
+    void flush(
+            const std::list<std::unique_ptr<C2Work>> &flushedWork) override;
+
+    std::unique_ptr<OutputBuffers> toArrayMode(size_t size) override;
+
+    size_t numClientBuffers() const final;
+
+    /**
+     * Return an appropriate Codec2Buffer object for the type of buffers.
+     *
+     * \param buffer  C2Buffer object to wrap.
+     *
+     * \return  appropriate Codec2Buffer object to wrap |buffer|.
+     */
+    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  appropriate Codec2Buffer object which can copy() from C2Buffers.
+     */
+    virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
+
+private:
+    FlexBuffersImpl mImpl;
+};
+
+class LinearOutputBuffers : public FlexOutputBuffers {
+public:
+    LinearOutputBuffers(const char *componentName, const char *name = "1D-Output")
+        : FlexOutputBuffers(componentName, name) { }
+
+    void flush(
+            const std::list<std::unique_ptr<C2Work>> &flushedWork) override;
+
+    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
+
+    sp<Codec2Buffer> allocateArrayBuffer() override;
+};
+
+class GraphicOutputBuffers : public FlexOutputBuffers {
+public:
+    GraphicOutputBuffers(const char *componentName, const char *name = "2D-Output")
+        : FlexOutputBuffers(componentName, name) { }
+
+    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
+
+    sp<Codec2Buffer> allocateArrayBuffer() override;
+};
+
+class RawGraphicOutputBuffers : public FlexOutputBuffers {
+public:
+    RawGraphicOutputBuffers(
+            size_t numOutputSlots, const char *componentName, const char *name = "2D-BB-Output");
+    ~RawGraphicOutputBuffers() override = default;
+
+    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
+
+    sp<Codec2Buffer> allocateArrayBuffer() override;
+
+private:
+    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
+}  // namespace android
+
+#endif  // CCODEC_BUFFERS_H_