Merge "codec2: make codec2_vndk a shared library"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 44ed034..bb517aa 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -622,7 +622,7 @@
     fprintf(stderr, "       -o playback audio\n");
     fprintf(stderr, "       -w(rite) filename (write to .mp4 file)\n");
     fprintf(stderr, "       -k seek test\n");
-    fprintf(stderr, "       -O(verride) name of the component\n");
+    fprintf(stderr, "       -N(ame) of the component\n");
     fprintf(stderr, "       -x display a histogram of decoding times/fps "
                     "(video only)\n");
     fprintf(stderr, "       -q don't show progress indicator\n");
@@ -708,7 +708,7 @@
     sp<ALooper> looper;
 
     int res;
-    while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kO:xSTd:D:")) >= 0) {
+    while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kN:xSTd:D:")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -737,7 +737,7 @@
                 break;
             }
 
-            case 'O':
+            case 'N':
             {
                 gComponentNameOverride.setTo(optarg);
                 break;
diff --git a/include/media/IMediaCodecService.h b/include/media/IMediaCodecService.h
deleted file mode 120000
index 37f6822..0000000
--- a/include/media/IMediaCodecService.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmedia/include/media/IMediaCodecService.h
\ No newline at end of file
diff --git a/include/media/MediaDefs.h b/include/media/MediaDefs.h
deleted file mode 120000
index 9850603..0000000
--- a/include/media/MediaDefs.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmedia/include/media/MediaDefs.h
\ No newline at end of file
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 26de4ca..484e310 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -117,9 +117,6 @@
         "android.hardware.media.omx@1.0",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.mapper@2.0",
-
-        // XXX: hack
-        "libstagefright_soft_c2avcdec",
     ],
 
     static_libs: [
@@ -135,7 +132,6 @@
         "libstagefright_id3",
         "libFLAC",
 
-        // XXX: hack
         "libstagefright_codec2_vndk",
     ],
 
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
index 068ca5f..0103abd 100644
--- a/media/libstagefright/CCodec.cpp
+++ b/media/libstagefright/CCodec.cpp
@@ -18,11 +18,10 @@
 #define LOG_TAG "CCodec"
 #include <utils/Log.h>
 
-// XXX: HACK
-#include "codecs/avcdec/C2SoftAvcDec.h"
-
 #include <thread>
 
+#include <C2PlatformSupport.h>
+
 #include <gui/Surface.h>
 #include <media/stagefright/CCodec.h>
 
@@ -181,8 +180,18 @@
     // TODO: use C2ComponentStore to create component
     mListener.reset(new CCodecListener(mChannel));
 
-    std::shared_ptr<C2Component> comp(new C2SoftAvcDec(componentName.c_str(), 0));
-    comp->setListener_vb(mListener, C2_DONT_BLOCK);
+    std::shared_ptr<C2Component> comp;
+    c2_status_t err = GetCodec2PlatformComponentStore()->createComponent(
+            componentName.c_str(), &comp);
+    if (err != C2_OK) {
+        Mutexed<State>::Locked state(mState);
+        state->mState = RELEASED;
+        state.unlock();
+        mCallback->onError(err, ACTION_CODE_FATAL);
+        state.lock();
+        return;
+    }
+    comp->setListener_vb(mListener, C2_MAY_BLOCK);
     {
         Mutexed<State>::Locked state(mState);
         if (state->mState != ALLOCATING) {
@@ -233,6 +242,26 @@
             setSurface(surface);
         }
 
+        // XXX: hack
+        bool audio = mime.startsWithIgnoreCase("audio/");
+        if (encoder) {
+            outputFormat->setString("mime", mime);
+            inputFormat->setString("mime", AStringPrintf("%s/raw", audio ? "audio" : "video"));
+            if (audio) {
+                inputFormat->setInt32("channel-count", 1);
+                inputFormat->setInt32("sample-rate", 44100);
+                outputFormat->setInt32("channel-count", 1);
+                outputFormat->setInt32("sample-rate", 44100);
+            }
+        } else {
+            inputFormat->setString("mime", mime);
+            outputFormat->setString("mime", AStringPrintf("%s/raw", audio ? "audio" : "video"));
+            if (audio) {
+                outputFormat->setInt32("channel-count", 2);
+                outputFormat->setInt32("sample-rate", 44100);
+            }
+        }
+
         // TODO
 
         return OK;
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 61f3f3c..8eca7e7 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016, The Android Open Source Project
+ * Copyright 2017, 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.
@@ -47,18 +47,11 @@
 using namespace hardware::cas::V1_0;
 using namespace hardware::cas::native::V1_0;
 
+namespace {
+
 // TODO: get this info from component
 const static size_t kMinBufferArraySize = 16;
 
-void CCodecBufferChannel::OutputBuffers::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.
-}
-
-namespace {
-
 template <class T>
 ssize_t findBufferSlot(
         std::vector<T> *buffers,
@@ -76,16 +69,103 @@
     return std::distance(buffers->begin(), it);
 }
 
+sp<Codec2Buffer> allocateLinearBuffer(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const sp<AMessage> &format,
+        size_t size,
+        const C2MemoryUsage &usage) {
+    std::shared_ptr<C2LinearBlock> block;
+
+    status_t err = pool->fetchLinearBlock(
+            size,
+            usage,
+            &block);
+    if (err != OK) {
+        return nullptr;
+    }
+
+    return Codec2Buffer::allocate(format, block);
+}
+
 class LinearBuffer : public C2Buffer {
 public:
     explicit LinearBuffer(C2ConstLinearBlock block) : C2Buffer({ block }) {}
 };
 
+class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
+public:
+    InputBuffersArray() = default;
+
+    void add(
+            size_t index,
+            const sp<MediaCodecBuffer> &clientBuffer,
+            const std::shared_ptr<C2Buffer> &compBuffer,
+            bool available) {
+        if (mBufferArray.size() < index) {
+            mBufferArray.resize(index + 1);
+        }
+        mBufferArray[index].clientBuffer = clientBuffer;
+        mBufferArray[index].compBuffer = compBuffer;
+        mBufferArray[index].available = available;
+    }
+
+    bool isArrayMode() final { return true; }
+
+    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
+        return nullptr;
+    }
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) final {
+        array->clear();
+        for (const auto &entry : mBufferArray) {
+            array->push(entry.clientBuffer);
+        }
+    }
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
+        for (size_t i = 0; i < mBufferArray.size(); ++i) {
+            if (mBufferArray[i].available) {
+                mBufferArray[i].available = false;
+                *index = i;
+                *buffer = mBufferArray[i].clientBuffer;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+        for (size_t i = 0; i < mBufferArray.size(); ++i) {
+            if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
+                mBufferArray[i].available = true;
+                return std::move(mBufferArray[i].compBuffer);
+            }
+        }
+        return nullptr;
+    }
+
+    void flush() override {
+        for (size_t i = 0; i < mBufferArray.size(); ++i) {
+            mBufferArray[i].available = true;
+            mBufferArray[i].compBuffer.reset();
+        }
+    }
+
+private:
+    struct Entry {
+        sp<MediaCodecBuffer> clientBuffer;
+        std::shared_ptr<C2Buffer> compBuffer;
+        bool available;
+    };
+
+    std::vector<Entry> mBufferArray;
+};
+
 class LinearInputBuffers : public CCodecBufferChannel::InputBuffers {
 public:
     using CCodecBufferChannel::InputBuffers::InputBuffers;
 
-    virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
         *buffer = nullptr;
         ssize_t ret = findBufferSlot<wp<Codec2Buffer>>(
                 &mBuffers, kMinBufferArraySize,
@@ -93,25 +173,20 @@
         if (ret < 0) {
             return false;
         }
-        std::shared_ptr<C2LinearBlock> block;
-
-        status_t err = mAlloc->fetchLinearBlock(
-                // TODO: proper max input size
-                65536,
-                { 0, C2MemoryUsage::CPU_WRITE },
-                &block);
-        if (err != OK) {
+        // TODO: proper max input size and usage
+        // TODO: read usage from intf
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        sp<Codec2Buffer> newBuffer = allocateLinearBuffer(mPool, mFormat, 65536, usage);
+        if (newBuffer == nullptr) {
             return false;
         }
-
-        sp<Codec2Buffer> newBuffer = Codec2Buffer::allocate(mFormat, block);
         mBuffers[ret] = newBuffer;
         *index = ret;
         *buffer = newBuffer;
         return true;
     }
 
-    virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+    std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
         auto it = std::find(mBuffers.begin(), mBuffers.end(), buffer);
         if (it == mBuffers.end()) {
             return nullptr;
@@ -122,80 +197,358 @@
         return std::make_shared<LinearBuffer>(codecBuffer->share());
     }
 
-    virtual void flush() override {
+    void flush() override {
+    }
+
+    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
+        std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
+        // TODO
+        const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
+        for (size_t i = 0; i < size; ++i) {
+            sp<Codec2Buffer> clientBuffer = mBuffers[i].promote();
+            bool available = false;
+            if (clientBuffer == nullptr) {
+                // TODO: proper max input size
+                // TODO: read usage from intf
+                C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+                clientBuffer = allocateLinearBuffer(mPool, mFormat, 65536, usage);
+                available = true;
+            }
+            array->add(
+                    i,
+                    clientBuffer,
+                    std::make_shared<LinearBuffer>(clientBuffer->share()),
+                    available);
+        }
+        return std::move(array);
     }
 
 private:
     // Buffers we passed to the client. The index of a buffer matches what
     // was passed in BufferCallback::onInputBufferAvailable().
     std::vector<wp<Codec2Buffer>> mBuffers;
-
-    // Buffer array we passed to the client. This only gets initialized at
-    // getInput/OutputBufferArray() and when this is set we can't add more
-    // buffers.
-    std::vector<sp<Codec2Buffer>> mBufferArray;
 };
 
-class GraphicOutputBuffers : public CCodecBufferChannel::OutputBuffers {
+// TODO: stub
+class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
+public:
+    using CCodecBufferChannel::InputBuffers::InputBuffers;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
+        (void)index;
+        (void)buffer;
+        return false;
+    }
+
+    std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+        (void)buffer;
+        return nullptr;
+    }
+
+    void flush() override {
+    }
+
+    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
+        return nullptr;
+    }
+};
+
+class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
 public:
     using CCodecBufferChannel::OutputBuffers::OutputBuffers;
 
-    virtual bool registerBuffer(
+    void add(
+            size_t index,
+            const sp<MediaCodecBuffer> &clientBuffer,
+            const std::shared_ptr<C2Buffer> &compBuffer,
+            bool available) {
+        if (mBufferArray.size() < index) {
+            mBufferArray.resize(index + 1);
+        }
+        mBufferArray[index].clientBuffer = clientBuffer;
+        mBufferArray[index].compBuffer = compBuffer;
+        mBufferArray[index].available = available;
+    }
+
+    bool isArrayMode() final { return true; }
+
+    std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() final {
+        return nullptr;
+    }
+
+    bool registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *codecBuffer) final {
+        for (size_t i = 0; i < mBufferArray.size(); ++i) {
+            if (mBufferArray[i].available && copy(buffer, mBufferArray[i].clientBuffer)) {
+                *index = i;
+                *codecBuffer = mBufferArray[i].clientBuffer;
+                mBufferArray[i].compBuffer = buffer;
+                mBufferArray[i].available = false;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool registerCsd(
+            const C2StreamCsdInfo::output *csd,
+            size_t *index,
+            sp<MediaCodecBuffer> *codecBuffer) final {
+        for (size_t i = 0; i < mBufferArray.size(); ++i) {
+            if (mBufferArray[i].available
+                    && mBufferArray[i].clientBuffer->capacity() <= csd->flexCount()) {
+                memcpy(mBufferArray[i].clientBuffer->base(), csd->m.value, csd->flexCount());
+                *index = i;
+                *codecBuffer = mBufferArray[i].clientBuffer;
+                mBufferArray[i].available = false;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) final {
+        for (size_t i = 0; i < mBufferArray.size(); ++i) {
+            if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
+                mBufferArray[i].available = true;
+                return std::move(mBufferArray[i].compBuffer);
+            }
+        }
+        return nullptr;
+    }
+
+    void flush(
+            const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
+        (void) flushedWork;
+        for (size_t i = 0; i < mBufferArray.size(); ++i) {
+            mBufferArray[i].available = true;
+            mBufferArray[i].compBuffer.reset();
+        }
+    }
+
+    virtual bool copy(
+            const std::shared_ptr<C2Buffer> &buffer,
+            const sp<MediaCodecBuffer> &clientBuffer) = 0;
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) final {
+        array->clear();
+        for (const auto &entry : mBufferArray) {
+            array->push(entry.clientBuffer);
+        }
+    }
+
+private:
+    struct Entry {
+        sp<MediaCodecBuffer> clientBuffer;
+        std::shared_ptr<C2Buffer> compBuffer;
+        bool available;
+    };
+
+    std::vector<Entry> mBufferArray;
+};
+
+class LinearOutputBuffersArray : public OutputBuffersArray {
+public:
+    using OutputBuffersArray::OutputBuffersArray;
+
+    bool copy(
+            const std::shared_ptr<C2Buffer> &buffer,
+            const sp<MediaCodecBuffer> &clientBuffer) final {
+        if (!buffer) {
+            clientBuffer->setRange(0u, 0u);
+            return true;
+        }
+        C2ReadView view = buffer->data().linearBlocks().front().map().get();
+        if (clientBuffer->capacity() < view.capacity()) {
+            return false;
+        }
+        clientBuffer->setRange(0u, view.capacity());
+        memcpy(clientBuffer->data(), view.data(), view.capacity());
+        return true;
+    }
+};
+
+class GraphicOutputBuffersArray : public OutputBuffersArray {
+public:
+    using OutputBuffersArray::OutputBuffersArray;
+
+    bool copy(
+            const std::shared_ptr<C2Buffer> &buffer,
+            const sp<MediaCodecBuffer> &clientBuffer) final {
+        if (!buffer) {
+            clientBuffer->setRange(0u, 0u);
+            return true;
+        }
+        clientBuffer->setRange(0u, 1u);
+        return true;
+    }
+};
+
+// Flexible in a sense that it does not have fixed array size.
+class FlexOutputBuffers : public CCodecBufferChannel::OutputBuffers {
+public:
+    using CCodecBufferChannel::OutputBuffers::OutputBuffers;
+
+    bool registerBuffer(
             const std::shared_ptr<C2Buffer> &buffer,
             size_t *index,
             sp<MediaCodecBuffer> *codecBuffer) override {
         *codecBuffer = nullptr;
         ssize_t ret = findBufferSlot<BufferInfo>(
                 &mBuffers,
-                kMinBufferArraySize,
-                [] (const auto &elem) { return elem.mClientBuffer.promote() == nullptr; });
+                std::numeric_limits<size_t>::max(),
+                [] (const auto &elem) { return elem.clientBuffer.promote() == nullptr; });
         if (ret < 0) {
             return false;
         }
         sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(
                 mFormat,
-                buffer == nullptr ? kEmptyBuffer : kDummyBuffer);
+                convert(buffer));
         mBuffers[ret] = { newBuffer, buffer };
         *index = ret;
         *codecBuffer = newBuffer;
         return true;
     }
 
-    virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+    bool registerCsd(
+            const C2StreamCsdInfo::output *csd,
+            size_t *index,
+            sp<MediaCodecBuffer> *codecBuffer) final {
+        *codecBuffer = nullptr;
+        ssize_t ret = findBufferSlot<BufferInfo>(
+                &mBuffers,
+                std::numeric_limits<size_t>::max(),
+                [] (const auto &elem) { return elem.clientBuffer.promote() == nullptr; });
+        if (ret < 0) {
+            return false;
+        }
+        sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(
+                mFormat,
+                ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
+        mBuffers[ret] = { newBuffer, nullptr };
+        *index = ret;
+        *codecBuffer = newBuffer;
+        return true;
+    }
+
+    std::shared_ptr<C2Buffer> releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer) override {
         auto it = std::find_if(
                 mBuffers.begin(), mBuffers.end(),
                 [buffer] (const auto &elem) {
-                    return elem.mClientBuffer.promote() == buffer;
+                    return elem.clientBuffer.promote() == buffer;
                 });
         if (it == mBuffers.end()) {
             return nullptr;
         }
-        return it->mBufferRef;
+        return std::move(it->bufferRef);
     }
 
-private:
-    static const sp<ABuffer> kEmptyBuffer;
-    static const sp<ABuffer> kDummyBuffer;
+    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.
+    }
 
+    virtual sp<ABuffer> convert(const std::shared_ptr<C2Buffer> &buffer) = 0;
+
+protected:
     struct BufferInfo {
         // wp<> of MediaCodecBuffer for MediaCodec.
-        wp<MediaCodecBuffer> mClientBuffer;
-        // Buffer reference to hold until mClientBuffer is valid.
-        std::shared_ptr<C2Buffer> mBufferRef;
+        wp<MediaCodecBuffer> clientBuffer;
+        // Buffer reference to hold until clientBuffer is valid.
+        std::shared_ptr<C2Buffer> bufferRef;
     };
     // Buffers we passed to the client. The index of a buffer matches what
     // was passed in BufferCallback::onInputBufferAvailable().
     std::vector<BufferInfo> mBuffers;
 };
 
-const sp<ABuffer> GraphicOutputBuffers::kEmptyBuffer = new ABuffer(nullptr, 0);
-const sp<ABuffer> GraphicOutputBuffers::kDummyBuffer = new ABuffer(nullptr, 1);
+class LinearOutputBuffers : public FlexOutputBuffers {
+public:
+    using FlexOutputBuffers::FlexOutputBuffers;
+
+    virtual sp<ABuffer> convert(const std::shared_ptr<C2Buffer> &buffer) override {
+        if (buffer == nullptr) {
+            return new ABuffer(nullptr, 0);
+        }
+        if (buffer->data().type() != C2BufferData::LINEAR) {
+            // We expect linear output buffers from the component.
+            return nullptr;
+        }
+        if (buffer->data().linearBlocks().size() != 1u) {
+            // We expect one and only one linear block from the component.
+            return nullptr;
+        }
+        C2ReadView view = buffer->data().linearBlocks().front().map().get();
+        if (view.error() != C2_OK) {
+            // Mapping the linear block failed
+            return nullptr;
+        }
+        return new ABuffer(
+                // XXX: the data is supposed to be read-only. We don't have
+                // const equivalent of ABuffer however...
+                const_cast<uint8_t *>(view.data()),
+                view.capacity());
+    }
+
+    std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
+        std::unique_ptr<OutputBuffersArray> array(new LinearOutputBuffersArray);
+
+        const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
+        for (size_t i = 0; i < size; ++i) {
+            sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
+            std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
+            bool available = false;
+            if (clientBuffer == nullptr) {
+                // TODO: proper max input size
+                clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(65536));
+                available = true;
+                compBuffer.reset();
+            }
+            array->add(i, clientBuffer, compBuffer, available);
+        }
+        return std::move(array);
+    }
+};
+
+class GraphicOutputBuffers : public FlexOutputBuffers {
+public:
+    using FlexOutputBuffers::FlexOutputBuffers;
+
+    sp<ABuffer> convert(const std::shared_ptr<C2Buffer> &buffer) override {
+        return buffer ? new ABuffer(nullptr, 1) : new ABuffer(nullptr, 0);
+    }
+
+    std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
+        std::unique_ptr<OutputBuffersArray> array(new GraphicOutputBuffersArray);
+
+        const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
+        for (size_t i = 0; i < size; ++i) {
+            sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
+            std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
+            bool available = false;
+            if (clientBuffer == nullptr) {
+                clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(nullptr, 1));
+                available = true;
+                compBuffer.reset();
+            }
+            array->add(i, clientBuffer, compBuffer, available);
+        }
+        return std::move(array);
+    }
+};
 
 }  // namespace
 
 CCodecBufferChannel::QueueGuard::QueueGuard(
         CCodecBufferChannel::QueueSync &sync) : mSync(sync) {
     std::unique_lock<std::mutex> l(mSync.mMutex);
+    // At this point it's guaranteed that mSync is not under state transition,
+    // as we are holding its mutex.
     if (mSync.mCount == -1) {
         mRunning = false;
     } else {
@@ -206,6 +559,8 @@
 
 CCodecBufferChannel::QueueGuard::~QueueGuard() {
     if (mRunning) {
+        // We are not holding mutex at this point so that QueueSync::stop() can
+        // keep holding the lock until mCount reaches zero.
         --mSync.mCount;
     }
 }
@@ -214,7 +569,7 @@
     std::unique_lock<std::mutex> l(mMutex);
     // If stopped, it goes to running state; otherwise no-op.
     int32_t expected = -1;
-    mCount.compare_exchange_strong(expected, 0);
+    (void)mCount.compare_exchange_strong(expected, 0);
 }
 
 void CCodecBufferChannel::QueueSync::stop() {
@@ -223,6 +578,11 @@
         // no-op
         return;
     }
+    // Holding mutex here blocks creation of additional QueueGuard objects, so
+    // mCount can only decrement. In other words, threads that acquired the lock
+    // are allowed to finish execution but additional threads trying to acquire
+    // the lock at this point will block, and then get QueueGuard at STOPPED
+    // state.
     int32_t expected = 0;
     while (!mCount.compare_exchange_weak(expected, -1)) {
         std::this_thread::yield();
@@ -232,8 +592,6 @@
 CCodecBufferChannel::CCodecBufferChannel(
         const std::function<void(status_t, enum ActionCode)> &onError)
     : mOnError(onError),
-      mInputBuffers(new LinearInputBuffers),
-      mOutputBuffers(new GraphicOutputBuffers),
       mFrameIndex(0u),
       mFirstValidFrameIndex(0u) {
 }
@@ -246,12 +604,50 @@
 
 void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &component) {
     mComponent = component;
-    // TODO: get pool ID from params
-    std::shared_ptr<C2BlockPool> pool;
-    c2_status_t err = GetCodec2BlockPool(C2BlockPool::BASIC_LINEAR, component, &pool);
-    if (err == C2_OK) {
+    C2StreamFormatConfig::input inputFormat(0u);
+    C2StreamFormatConfig::output outputFormat(0u);
+    c2_status_t err = mComponent->intf()->query_vb(
+            { &inputFormat, &outputFormat },
+            {},
+            C2_DONT_BLOCK,
+            nullptr);
+    if (err != C2_OK) {
+        // TODO: error
+        return;
+    }
+
+    {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        (*buffers)->setAlloc(pool);
+
+        bool graphic = (inputFormat.value == C2FormatVideo);
+        if (graphic) {
+            buffers->reset(new GraphicInputBuffers);
+        } else {
+            buffers->reset(new LinearInputBuffers);
+        }
+
+        ALOGV("graphic = %s", graphic ? "true" : "false");
+        std::shared_ptr<C2BlockPool> pool;
+        err = GetCodec2BlockPool(
+                graphic ? C2BlockPool::BASIC_GRAPHIC : C2BlockPool::BASIC_LINEAR,
+                component,
+                &pool);
+        if (err == C2_OK) {
+            (*buffers)->setPool(pool);
+        } else {
+            // TODO: error
+        }
+    }
+
+    {
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+
+        bool graphic = (outputFormat.value == C2FormatVideo);
+        if (graphic) {
+            buffers->reset(new GraphicOutputBuffers);
+        } else {
+            buffers->reset(new LinearOutputBuffers);
+        }
     }
 }
 
@@ -314,17 +710,6 @@
 status_t CCodecBufferChannel::renderOutputBuffer(
         const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
     ALOGV("renderOutputBuffer");
-    sp<MediaCodecBuffer> inBuffer;
-    size_t index;
-    {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
-            inBuffer = nullptr;
-        }
-    }
-    if (inBuffer != nullptr) {
-        mCallback->onInputBufferAvailable(index, inBuffer);
-    }
 
     std::shared_ptr<C2Buffer> c2Buffer;
     {
@@ -385,85 +770,38 @@
 }
 
 status_t CCodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer) {
-    ALOGV("discardBuffer");
+    ALOGV("discardBuffer: %p", buffer.get());
     {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        (void) (*buffers)->releaseBuffer(buffer);
+        (void)(*buffers)->releaseBuffer(buffer);
     }
     {
         Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        (void) (*buffers)->releaseBuffer(buffer);
+        (void)(*buffers)->releaseBuffer(buffer);
     }
     return OK;
 }
 
-#if 0
-void fillBufferArray_l(Mutexed<Buffers>::Locked &buffers) {
-    for (size_t i = 0; i < buffers->mClientBuffer.size(); ++i) {
-        sp<Codec2Buffer> buffer(buffers->mClientBuffer.get(i).promote());
-        if (buffer == nullptr) {
-            buffer = allocateBuffer_l(buffers->mAlloc);
-        }
-        buffers->mBufferArray.push_back(buffer);
-    }
-    while (buffers->mBufferArray.size() < kMinBufferArraySize) {
-        sp<Codec2Buffer> buffer = allocateBuffer_l(buffers->mAlloc);
-        // allocate buffer
-        buffers->mBufferArray.push_back(buffer);
-    }
-}
-#endif
-
 void CCodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
-    (void) array;
-    // TODO
-#if 0
     array->clear();
-    Mutexed<Buffers>::Locked buffers(mInputBuffers);
+    Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
 
-    if (!buffers->isArrayMode()) {
-        // mBufferArray is empty.
-        fillBufferArray_l(buffers);
+    if (!(*buffers)->isArrayMode()) {
+        *buffers = (*buffers)->toArrayMode();
     }
 
-    for (const auto &buffer : buffers->mBufferArray) {
-        array->push_back(buffer);
-    }
-#endif
+    (*buffers)->getArray(array);
 }
 
 void CCodecBufferChannel::getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
-    (void) array;
-    // TODO
-#if 0
     array->clear();
-    Mutexed<Buffers>::Locked buffers(mOutputBuffers);
+    Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
 
-    if (!buffers->isArrayMode()) {
-        if (linear) {
-            // mBufferArray is empty.
-            fillBufferArray_l(buffers);
-
-            // We need to replace the allocator so that the component only returns
-            // buffer from the array.
-            ArrayModeAllocator::Builder builder(buffers->mBufferArray);
-            for (size_t i = 0; i < buffers->mClientBuffer.size(); ++i) {
-                if (buffers->mClientBuffer.get(i).promote() != nullptr) {
-                    builder.markUsing(i);
-                }
-            }
-            buffers->mAlloc.reset(builder.build());
-        } else {
-            for (int i = 0; i < X; ++i) {
-                buffers->mBufferArray.push_back(dummy buffer);
-            }
-        }
+    if (!(*buffers)->isArrayMode()) {
+        *buffers = (*buffers)->toArrayMode();
     }
 
-    for (const auto &buffer : buffers->mBufferArray) {
-        array->push_back(buffer);
-    }
-#endif
+    (*buffers)->getArray(array);
 }
 
 void CCodecBufferChannel::start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
@@ -513,6 +851,19 @@
 
 void CCodecBufferChannel::onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems) {
     for (const auto &work : workItems) {
+        sp<MediaCodecBuffer> inBuffer;
+        size_t index;
+        {
+            Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+            if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
+                ALOGW("no new buffer available");
+                inBuffer = nullptr;
+            }
+        }
+        if (inBuffer != nullptr) {
+            mCallback->onInputBufferAvailable(index, inBuffer);
+        }
+
         if (work->result != OK) {
             ALOGE("work failed to complete: %d", work->result);
             mOnError(work->result, ACTION_CODE_FATAL);
@@ -539,7 +890,16 @@
         }
 
         const std::shared_ptr<C2Buffer> &buffer = worklet->output.buffers[0];
-        // TODO: transfer infos() into buffer metadata
+        const C2StreamCsdInfo::output *csdInfo = nullptr;
+        if (buffer) {
+            // TODO: transfer infos() into buffer metadata
+        }
+        for (const auto &info : worklet->output.infos) {
+            if (info->coreIndex() == C2StreamCsdInfo::output::CORE_INDEX) {
+                ALOGV("csd found");
+                csdInfo = static_cast<const C2StreamCsdInfo::output *>(info.get());
+            }
+        }
 
         int32_t flags = 0;
         if (worklet->output.flags & C2BufferPack::FLAG_END_OF_STREAM) {
@@ -547,15 +907,43 @@
             ALOGV("output EOS");
         }
 
-        size_t index;
         sp<MediaCodecBuffer> outBuffer;
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
-            ALOGE("unable to register output buffer");
-            mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+        if (csdInfo != nullptr) {
+            Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+            if ((*buffers)->registerCsd(csdInfo, &index, &outBuffer)) {
+                outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp);
+                outBuffer->meta()->setInt32("flags", flags | MediaCodec::BUFFER_FLAG_CODECCONFIG);
+                ALOGV("csd index = %zu", index);
+
+                buffers.unlock();
+                mCallback->onOutputBufferAvailable(index, outBuffer);
+                buffers.lock();
+            } else {
+                ALOGE("unable to register output buffer");
+                buffers.unlock();
+                mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+                buffers.lock();
+                continue;
+            }
+        }
+
+        if (!buffer && !flags) {
+            ALOGV("Not reporting output buffer");
             continue;
         }
 
+        {
+            Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+            if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
+                ALOGE("unable to register output buffer");
+
+                buffers.unlock();
+                mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+                buffers.lock();
+                continue;
+            }
+        }
+
         outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp);
         outBuffer->meta()->setInt32("flags", flags);
         ALOGV("index = %zu", index);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 6ad11f4..7cfa4ce 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -552,7 +552,7 @@
 //static
 sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
     static bool ccodecEnabled = property_get_bool("debug.stagefright.ccodec", false);
-    if (ccodecEnabled && !nameIsType && name.startsWithIgnoreCase("codec2.")) {
+    if (ccodecEnabled && !nameIsType && name.startsWithIgnoreCase("c2.")) {
         return new CCodec;
     } else if (nameIsType || name.startsWithIgnoreCase("omx.")) {
         // at this time only ACodec specifies a mime type.
diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h
index bbbf338..83cb72c 100644
--- a/media/libstagefright/codec2/include/C2Config.h
+++ b/media/libstagefright/codec2/include/C2Config.h
@@ -66,6 +66,8 @@
     kParamIndexMaxVideoSizeHint,
     kParamIndexVideoSizeTuning,
 
+    kParamIndexCsd,
+
     // video info
 
     kParamIndexStructStart = 0x1,
@@ -129,6 +131,8 @@
 
 typedef C2PortParam<C2Tuning, C2Uint64Array, kParamIndexBlockPools> C2PortBlockPoolsTuning;
 
+typedef C2StreamParam<C2Info, C2BlobValue, kParamIndexCsd> C2StreamCsdInfo;
+
 /*
    Component description fields:
 
diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h
index 354cee2..c5062d6 100644
--- a/media/libstagefright/include/CCodecBufferChannel.h
+++ b/media/libstagefright/include/CCodecBufferChannel.h
@@ -33,24 +33,36 @@
 namespace android {
 
 /**
- * BufferChannelBase implementation for ACodec.
+ * BufferChannelBase implementation for CCodec.
  */
 class CCodecBufferChannel : public BufferChannelBase {
 public:
+    /**
+     * Base class for representation of buffers at one port.
+     */
     class Buffers {
     public:
         Buffers() = default;
         virtual ~Buffers() = default;
 
-        inline void setAlloc(const std::shared_ptr<C2BlockPool> &alloc) { mAlloc = alloc; }
+        /**
+         * Set format for MediaCodec-facing buffers.
+         */
         inline void setFormat(const sp<AMessage> &format) { mFormat = format; }
-        inline const std::shared_ptr<C2BlockPool> &getAlloc() { return mAlloc; }
+
+        /**
+         * Returns true if the buffers are operating under array mode.
+         */
+        virtual bool isArrayMode() { return false; }
+
+        /**
+         * Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
+         * no-op.
+         */
+        virtual void getArray(Vector<sp<MediaCodecBuffer>> *) {}
 
     protected:
-        // Input: this object uses it to allocate input buffers with which the
-        // client fills.
-        // Output: this object passes it to the component.
-        std::shared_ptr<C2BlockPool> mAlloc;
+        // Format to be used for creating MediaCodec-facing buffers.
         sp<AMessage> mFormat;
 
     private:
@@ -62,10 +74,41 @@
         using Buffers::Buffers;
         virtual ~InputBuffers() = default;
 
+        /**
+         * Set a block pool to obtain input memory blocks.
+         */
+        inline 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 empty shared_ptr if the
+         * buffer is not on file.
+         */
         virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 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() = 0;
+
+    protected:
+        // Pool to obtain blocks for input buffers.
+        std::shared_ptr<C2BlockPool> mPool;
+
     private:
         DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
     };
@@ -75,12 +118,46 @@
         using Buffers::Buffers;
         virtual ~OutputBuffers() = default;
 
+        /**
+         * Register output C2Buffer from the component and obtain corresponding
+         * index and MediaCodecBuffer object. Returns false if registration
+         * fails.
+         */
         virtual bool registerBuffer(
                 const std::shared_ptr<C2Buffer> &buffer,
                 size_t *index,
                 sp<MediaCodecBuffer> *codecBuffer) = 0;
+
+        /**
+         * Register codec specific data as a buffer to be consistent with
+         * MediaCodec behavior.
+         */
+        virtual bool registerCsd(
+                const C2StreamCsdInfo::output * /* csd */,
+                size_t * /* index */,
+                sp<MediaCodecBuffer> * /* codecBuffer */) {
+            return false;
+        }
+
+        /**
+         * Release the buffer obtained from registerBuffer() and get the
+         * associated C2Buffer object back. Returns empty shared_ptr if the
+         * buffer is not on file.
+         */
         virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
-        virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
+
+        /**
+         * 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() = 0;
 
     private:
         DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
@@ -151,12 +228,34 @@
 private:
     class QueueGuard;
 
+    /**
+     * Special mutex-like object with the following properties:
+     *
+     * - At STOPPED state (initial, or after stop())
+     *   - QueueGuard object gets created at STOPPED state, and the client is
+     *     supposed to return immediately.
+     * - At RUNNING state (after start())
+     *   - Each QueueGuard object
+     */
     class QueueSync {
     public:
+        /**
+         * At construction the sync object is in STOPPED state.
+         */
         inline QueueSync() : mCount(-1) {}
         ~QueueSync() = default;
 
+        /**
+         * Transition to RUNNING state when stopped. No-op if already in RUNNING
+         * state.
+         */
         void start();
+
+        /**
+         * At RUNNING state, wait until all QueueGuard object created during
+         * RUNNING state are destroyed, and then transition to STOPPED state.
+         * No-op if already in STOPPED state.
+         */
         void stop();
 
     private:
@@ -186,6 +285,7 @@
     std::function<void(status_t, enum ActionCode)> mOnError;
     std::shared_ptr<C2BlockPool> mInputAllocator;
     QueueSync mQueueSync;
+
     Mutexed<std::unique_ptr<InputBuffers>> mInputBuffers;
     Mutexed<std::unique_ptr<OutputBuffers>> mOutputBuffers;
 
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index ca31691..e61565b 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -2,7 +2,10 @@
 
 # service executable
 include $(CLEAR_VARS)
+# seccomp is not required for coverage build.
+ifneq ($(NATIVE_COVERAGE),true)
 LOCAL_REQUIRED_MODULES_arm := mediacodec.policy
+endif
 LOCAL_SRC_FILES := main_codecservice.cpp
 LOCAL_SHARED_LIBRARIES := \
     libmedia_omx \