Mark operator c2_cntr64_t as constexpr
am: a52cd7fb5a

Change-Id: If2d333257d24446d0ba3ca2b3297b73fd7b87343
diff --git a/codec2/vndk/C2AllocatorGralloc.cpp b/codec2/vndk/C2AllocatorGralloc.cpp
index 1b57c72..b0ec5f1 100644
--- a/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/codec2/vndk/C2AllocatorGralloc.cpp
@@ -303,17 +303,18 @@
 }
 
 C2AllocationGralloc::~C2AllocationGralloc() {
-    if (!mBuffer) {
-        return;
-    }
-    if (mLocked) {
+    if (mBuffer && mLocked) {
         // implementation ignores addresss and rect
         uint8_t* addr[C2PlanarLayout::MAX_NUM_PLANES] = {};
         unmap(addr, C2Rect(), nullptr);
     }
-    mMapper->freeBuffer(const_cast<native_handle_t *>(mBuffer));
-    native_handle_delete(const_cast<native_handle_t*>(
-            reinterpret_cast<const native_handle_t*>(mHandle)));
+    if (mBuffer) {
+        mMapper->freeBuffer(const_cast<native_handle_t *>(mBuffer));
+    }
+    if (mHandle) {
+        native_handle_delete(
+                const_cast<native_handle_t *>(reinterpret_cast<const native_handle_t *>(mHandle)));
+    }
 }
 
 c2_status_t C2AllocationGralloc::map(
diff --git a/media/codecs/aac/C2SoftAacEnc.cpp b/media/codecs/aac/C2SoftAacEnc.cpp
index b41499f..b4d7d9d 100644
--- a/media/codecs/aac/C2SoftAacEnc.cpp
+++ b/media/codecs/aac/C2SoftAacEnc.cpp
@@ -417,12 +417,12 @@
         if (encoderErr == AACENC_OK) {
             if (outargs.numOutBytes > 0) {
                 mInputSize = 0;
-                int consumed = ((capacity / sizeof(int16_t)) - inargs.numInSamples);
+                int consumed = (capacity / sizeof(int16_t)) - inargs.numInSamples
+                        + outargs.numInSamples;
                 mInputTimeUs = work->input.ordinal.timestamp
                         + (consumed * 1000000ll / channelCount / sampleRate);
             } else {
                 mInputSize += outargs.numInSamples * sizeof(int16_t);
-                mInputTimeUs += outargs.numInSamples * 1000000ll / channelCount / sampleRate;
             }
             outPtr += outargs.numOutBytes;
             nOutputBytes += outargs.numOutBytes;
diff --git a/media/sfplugin/CCodec.cpp b/media/sfplugin/CCodec.cpp
index baf9cb6..643137d 100644
--- a/media/sfplugin/CCodec.cpp
+++ b/media/sfplugin/CCodec.cpp
@@ -807,12 +807,16 @@
             config->mInputFormat->setInt32("using-sw-read-often", true);
         }
 
-        // use client specified input size if specified
-        bool clientInputSize = msg->findInt32(KEY_MAX_INPUT_SIZE, (int32_t*)&maxInputSize.value);
-
+        // NOTE: we don't blindly use client specified input size if specified as clients
+        // at times specify too small size. Instead, mimic the behavior from OMX, where the
+        // client specified size is only used to ask for bigger buffers than component suggested
+        // size.
+        int32_t clientInputSize = 0;
+        bool clientSpecifiedInputSize =
+            msg->findInt32(KEY_MAX_INPUT_SIZE, &clientInputSize) && clientInputSize > 0;
         // TEMP: enforce minimum buffer size of 1MB for video decoders
         // and 16K / 4K for audio encoders/decoders
-        if (!clientInputSize && maxInputSize.value == 0) {
+        if (maxInputSize.value == 0) {
             if (config->mDomain & Config::IS_AUDIO) {
                 maxInputSize.value = encoder ? 16384 : 4096;
             } else if (!encoder) {
@@ -832,20 +836,17 @@
 
         // TODO: do this based on component requiring linear allocator for input
         if ((config->mDomain & Config::IS_DECODER) || (config->mDomain & Config::IS_AUDIO)) {
-            // For audio decoder, override client's max input size if necessary.
-            if ((config->mDomain & Config::IS_DECODER) && (config->mDomain & Config::IS_AUDIO)) {
-                int32_t compSize;
-                if (config->mInputFormat->findInt32(KEY_MAX_INPUT_SIZE, &compSize)
-                        && maxInputSize.value > 0
-                        && compSize > 0
-                        && maxInputSize.value < (uint32_t)compSize) {
-                    ALOGD("client requested max input size %u, which is smaller than "
-                          "what component recommended (%d); overriding with component "
-                          "recommendation.", maxInputSize.value, compSize);
+            if (clientSpecifiedInputSize) {
+                // Warn that we're overriding client's max input size if necessary.
+                if ((uint32_t)clientInputSize < maxInputSize.value) {
+                    ALOGD("client requested max input size %d, which is smaller than "
+                          "what component recommended (%u); overriding with component "
+                          "recommendation.", clientInputSize, maxInputSize.value);
                     ALOGW("This behavior is subject to change. It is recommended that "
                           "app developers double check whether the requested "
                           "max input size is in reasonable range.");
-                    maxInputSize.value = compSize;
+                } else {
+                    maxInputSize.value = clientInputSize;
                 }
             }
             // Pass max input size on input format to the buffer channel (if supplied by the
@@ -1015,7 +1016,7 @@
         ALOGD("ISConfig: no configuration");
     }
 
-    return surface->start();
+    return OK;
 }
 
 void CCodec::initiateSetInputSurface(const sp<PersistentSurface> &surface) {
@@ -1102,12 +1103,20 @@
     }
     sp<AMessage> inputFormat;
     sp<AMessage> outputFormat;
+    status_t err2 = OK;
     {
         Mutexed<Config>::Locked config(mConfig);
         inputFormat = config->mInputFormat;
         outputFormat = config->mOutputFormat;
+        if (config->mInputSurface) {
+            err2 = config->mInputSurface->start();
+        }
     }
-    status_t err2 = mChannel->start(inputFormat, outputFormat);
+    if (err2 != OK) {
+        mCallback->onError(err2, ACTION_CODE_FATAL);
+        return;
+    }
+    err2 = mChannel->start(inputFormat, outputFormat);
     if (err2 != OK) {
         mCallback->onError(err2, ACTION_CODE_FATAL);
         return;
@@ -1182,6 +1191,13 @@
     }
 
     {
+        Mutexed<Config>::Locked config(mConfig);
+        if (config->mInputSurface) {
+            config->mInputSurface->disconnect();
+            config->mInputSurface = nullptr;
+        }
+    }
+    {
         Mutexed<State>::Locked state(mState);
         if (state->get() == STOPPING) {
             state->set(ALLOCATED);
@@ -1191,6 +1207,7 @@
 }
 
 void CCodec::initiateRelease(bool sendCallback /* = true */) {
+    bool clearInputSurfaceIfNeeded = false;
     {
         Mutexed<State>::Locked state(mState);
         if (state->get() == RELEASED || state->get() == RELEASING) {
@@ -1212,9 +1229,23 @@
             }
             return;
         }
+        if (state->get() == STARTING
+                || state->get() == RUNNING
+                || state->get() == STOPPING) {
+            // Input surface may have been started, so clean up is needed.
+            clearInputSurfaceIfNeeded = true;
+        }
         state->set(RELEASING);
     }
 
+    if (clearInputSurfaceIfNeeded) {
+        Mutexed<Config>::Locked config(mConfig);
+        if (config->mInputSurface) {
+            config->mInputSurface->disconnect();
+            config->mInputSurface = nullptr;
+        }
+    }
+
     mChannel->stop();
     // thiz holds strong ref to this while the thread is running.
     sp<CCodec> thiz(this);
diff --git a/media/sfplugin/CCodecBufferChannel.cpp b/media/sfplugin/CCodecBufferChannel.cpp
index 6e8bd90..31f133b 100644
--- a/media/sfplugin/CCodecBufferChannel.cpp
+++ b/media/sfplugin/CCodecBufferChannel.cpp
@@ -128,7 +128,9 @@
      * and released successfully.
      */
     virtual bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) = 0;
+            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
@@ -459,13 +461,18 @@
      * \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 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;
-                mBuffers[i].clientBuffer.clear();
+                if (release) {
+                    mBuffers[i].clientBuffer.clear();
+                }
                 index = i;
                 break;
             }
@@ -474,8 +481,11 @@
             ALOGV("[%s] %s: No matching buffer found", mName, __func__);
             return false;
         }
-        std::shared_ptr<C2Buffer> result = clientBuffer->asC2Buffer();
-        mBuffers[index].compBuffer = result;
+        std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
+        if (!result) {
+            result = clientBuffer->asC2Buffer();
+            mBuffers[index].compBuffer = result;
+        }
         if (c2buffer) {
             *c2buffer = result;
         }
@@ -489,8 +499,8 @@
             if (!compBuffer || compBuffer != c2buffer) {
                 continue;
             }
-            mBuffers[i].clientBuffer = nullptr;
             mBuffers[i].compBuffer.reset();
+            ALOGV("[%s] codec released buffer #%zu", mName, i);
             return true;
         }
         ALOGV("[%s] codec released an unknown buffer", mName);
@@ -593,7 +603,10 @@
      * \return  true  if the buffer is successfully returned
      *          false otherwise
      */
-    bool returnBuffer(const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) {
+    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) {
@@ -602,7 +615,9 @@
                     ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu", mName, i);
                 }
                 clientBuffer = mBuffers[i].clientBuffer;
-                mBuffers[i].ownedByClient = false;
+                if (release) {
+                    mBuffers[i].ownedByClient = false;
+                }
                 index = i;
                 break;
             }
@@ -612,8 +627,11 @@
             return false;
         }
         ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index);
-        std::shared_ptr<C2Buffer> result = clientBuffer->asC2Buffer();
-        mBuffers[index].compBuffer = result;
+        std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
+        if (!result) {
+            result = clientBuffer->asC2Buffer();
+            mBuffers[index].compBuffer = result;
+        }
         if (c2buffer) {
             *c2buffer = result;
         }
@@ -632,9 +650,9 @@
                     // This should not happen.
                     ALOGD("[%s] codec released a buffer owned by client "
                           "(index %zu)", mName, i);
-                    mBuffers[i].ownedByClient = false;
                 }
                 mBuffers[i].compBuffer.reset();
+                ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i);
                 return true;
             }
         }
@@ -711,8 +729,10 @@
     }
 
     bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.returnBuffer(buffer, c2buffer);
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override {
+        return mImpl.returnBuffer(buffer, c2buffer, release);
     }
 
     bool expireComponentBuffer(
@@ -753,8 +773,10 @@
     }
 
     bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.releaseSlot(buffer, c2buffer);
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override {
+        return mImpl.releaseSlot(buffer, c2buffer, release);
     }
 
     bool expireComponentBuffer(
@@ -771,8 +793,13 @@
     std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
             size_t size) final {
         int32_t capacity = kLinearBufferSize;
-        (void)mFormat->findInt32(C2_NAME_STREAM_MAX_BUFFER_SIZE_SETTING, &capacity);
-
+        (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);
@@ -784,7 +811,7 @@
         return std::move(array);
     }
 
-    virtual sp<Codec2Buffer> alloc(size_t size) const {
+    virtual sp<Codec2Buffer> alloc(size_t size) {
         C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
         std::shared_ptr<C2LinearBlock> block;
 
@@ -807,6 +834,7 @@
             const sp<MemoryDealer> &dealer,
             const sp<ICrypto> &crypto,
             int32_t heapSeqNum,
+            size_t capacity,
             const char *componentName, const char *name = "EncryptedInput")
         : LinearInputBuffers(componentName, name),
           mUsage({0, 0}),
@@ -819,7 +847,7 @@
             mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
         }
         for (size_t i = 0; i < kMinInputBufferArraySize; ++i) {
-            sp<IMemory> memory = mDealer->allocate(kLinearBufferSize);
+            sp<IMemory> memory = mDealer->allocate(capacity);
             if (memory == nullptr) {
                 ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated", mName, i);
                 break;
@@ -831,11 +859,12 @@
     ~EncryptedLinearInputBuffers() override {
     }
 
-    sp<Codec2Buffer> alloc(size_t size) const override {
+    sp<Codec2Buffer> alloc(size_t size) override {
         sp<IMemory> memory;
-        for (const Entry &entry : mMemoryVector) {
-            if (entry.block.expired()) {
-                memory = entry.memory;
+        size_t slot = 0;
+        for (; slot < mMemoryVector.size(); ++slot) {
+            if (mMemoryVector[slot].block.expired()) {
+                memory = mMemoryVector[slot].memory;
                 break;
             }
         }
@@ -845,10 +874,11 @@
 
         std::shared_ptr<C2LinearBlock> block;
         c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block);
-        if (err != C2_OK) {
+        if (err != C2_OK || block == nullptr) {
             return nullptr;
         }
 
+        mMemoryVector[slot].block = block;
         return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum);
     }
 
@@ -888,8 +918,10 @@
     }
 
     bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.releaseSlot(buffer, c2buffer);
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override {
+        return mImpl.releaseSlot(buffer, c2buffer, release);
     }
 
     bool expireComponentBuffer(
@@ -951,8 +983,10 @@
     }
 
     bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.releaseSlot(buffer, c2buffer);
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override {
+        return mImpl.releaseSlot(buffer, c2buffer, release);
     }
 
     bool expireComponentBuffer(
@@ -996,13 +1030,14 @@
     }
 
     bool releaseBuffer(
-            const sp<MediaCodecBuffer> &, std::shared_ptr<C2Buffer> *) override {
+            const sp<MediaCodecBuffer> &, std::shared_ptr<C2Buffer> *, bool) override {
         return false;
     }
 
     bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &) override {
         return false;
     }
+
     void flush() override {
     }
 
@@ -1088,7 +1123,7 @@
 
     bool releaseBuffer(
             const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.returnBuffer(buffer, c2buffer);
+        return mImpl.returnBuffer(buffer, c2buffer, true);
     }
 
     void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
@@ -1137,8 +1172,9 @@
     }
 
     bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.releaseSlot(buffer, c2buffer);
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer) override {
+        return mImpl.releaseSlot(buffer, c2buffer, true);
     }
 
     void flush(
@@ -1445,6 +1481,8 @@
       mMetaMode(MODE_NONE),
       mAvailablePipelineCapacity(),
       mInputMetEos(false) {
+    Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+    buffers->reset(new DummyInputBuffers(""));
 }
 
 CCodecBufferChannel::~CCodecBufferChannel() {
@@ -1507,7 +1545,7 @@
     if (buffer->size() > 0u) {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
         std::shared_ptr<C2Buffer> c2buffer;
-        if (!(*buffers)->releaseBuffer(buffer, &c2buffer)) {
+        if (!(*buffers)->releaseBuffer(buffer, &c2buffer, false)) {
             return -ENOENT;
         }
         work->input.buffers.push_back(c2buffer);
@@ -1544,6 +1582,10 @@
     }
     if (err == C2_OK) {
         mCCodecCallback->onWorkQueued(eos);
+
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        bool released = (*buffers)->releaseBuffer(buffer, nullptr, true);
+        ALOGV("[%s] queueInputBuffer: buffer %sreleased", mName, released ? "" : "not ");
     }
 
     feedInputBufferIfAvailableInternal();
@@ -1812,6 +1854,8 @@
         hdr.cta8613 = cta861_meta;
         qbi.setHdrMetadata(hdr);
     }
+    // we don't have dirty regions
+    qbi.setSurfaceDamage(Region::INVALID_REGION);
     android::IGraphicBufferProducer::QueueBufferOutput qbo;
     status_t result = mComponent->queueToOutputSurface(block, qbi, &qbo);
     if (result != OK) {
@@ -1832,7 +1876,7 @@
     bool released = false;
     {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr)) {
+        if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr, true)) {
             buffers.unlock();
             released = true;
             mAvailablePipelineCapacity.freeInputSlots(1, "discardBuffer");
@@ -1981,6 +2025,7 @@
             pools->inputPool = pool;
         }
 
+        bool forceArrayMode = false;
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
         if (graphic) {
             if (mInputSurface) {
@@ -1992,12 +2037,18 @@
             }
         } else {
             if (hasCryptoOrDescrambler()) {
+                int32_t capacity = kLinearBufferSize;
+                (void)inputFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
+                if ((size_t)capacity > kMaxLinearBufferSize) {
+                    ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
+                    capacity = kMaxLinearBufferSize;
+                }
                 if (mDealer == nullptr) {
                     mDealer = new MemoryDealer(
-                            align(kLinearBufferSize, MemoryDealer::getAllocationAlignment())
+                            align(capacity, MemoryDealer::getAllocationAlignment())
                                 * (kMinInputBufferArraySize + 1),
                             "EncryptedLinearInputBuffers");
-                    mDecryptDestination = mDealer->allocate(kLinearBufferSize);
+                    mDecryptDestination = mDealer->allocate((size_t)capacity);
                 }
                 if (mCrypto != nullptr && mHeapSeqNum < 0) {
                     mHeapSeqNum = mCrypto->setHeap(mDealer->getMemoryHeap());
@@ -2005,7 +2056,8 @@
                     mHeapSeqNum = -1;
                 }
                 buffers->reset(new EncryptedLinearInputBuffers(
-                        secure, mDealer, mCrypto, mHeapSeqNum, mName));
+                        secure, mDealer, mCrypto, mHeapSeqNum, (size_t)capacity, mName));
+                forceArrayMode = true;
             } else {
                 buffers->reset(new LinearInputBuffers(mName));
             }
@@ -2017,6 +2069,10 @@
         } else {
             // TODO: error
         }
+
+        if (forceArrayMode) {
+            *buffers = (*buffers)->toArrayMode(kMinInputBufferArraySize);
+        }
     }
 
     if (outputFormat != nullptr) {
@@ -2234,7 +2290,6 @@
     mSync.stop();
     mFirstValidFrameIndex = mFrameIndex.load();
     if (mInputSurface != nullptr) {
-        mInputSurface->disconnect();
         mInputSurface.reset();
     }
 }
diff --git a/media/sfplugin/Codec2InfoBuilder.cpp b/media/sfplugin/Codec2InfoBuilder.cpp
index bf6ee9c..d883d46 100644
--- a/media/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/sfplugin/Codec2InfoBuilder.cpp
@@ -44,6 +44,7 @@
 #include <cutils/native_handle.h>
 #include <media/omx/1.0/WOmxNode.h>
 #include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/foundation/ALookup.h>
 #include <media/stagefright/foundation/MediaDefs.h>
 #include <media/stagefright/omx/OMXUtils.h>
 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
@@ -350,7 +351,7 @@
     }
 
     bool surfaceTest(Codec2Client::CreateInputSurface());
-    if (option == 0 || !surfaceTest) {
+    if (option == 0 || (option != 4 && !surfaceTest)) {
         buildOmxInfo(parser, writer);
     }
 
@@ -407,6 +408,7 @@
             break;
         }
 
+        ALOGV("canonName = %s", canonName.c_str());
         std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
         codecInfo->setName(trait.name.c_str());
         codecInfo->setOwner("codec2");
@@ -450,6 +452,18 @@
                         asString(err), asString(profileQuery[0].status));
                 if (err == C2_OK && profileQuery[0].status == C2_OK) {
                     if (profileQuery[0].values.type == C2FieldSupportedValues::VALUES) {
+                        std::vector<std::shared_ptr<C2ParamDescriptor>> supportedParams;
+                        bool hdrSupported = false;
+                        err = intf->querySupportedParams(&supportedParams);
+                        if (err == C2_OK) {
+                            for (const std::shared_ptr<C2ParamDescriptor> &desc : supportedParams) {
+                                if (desc->index().coreIndex() == C2StreamHdrStaticInfo::CORE_INDEX) {
+                                    hdrSupported = true;
+                                    break;
+                                }
+                            }
+                        }
+                        ALOGV("HDR %ssupported", hdrSupported ? "" : "not ");
                         for (C2Value::Primitive profile : profileQuery[0].values.values) {
                             pl.profile = (C2Config::profile_t)profile.ref<uint32_t>();
                             std::vector<std::unique_ptr<C2SettingResult>> failures;
@@ -467,15 +481,31 @@
                                     C2Value::Primitive level = levelQuery[0].values.values.back();
                                     pl.level = (C2Config::level_t)level.ref<uint32_t>();
                                     ALOGV("supporting level: %u", pl.level);
+                                    bool added = false;
                                     int32_t sdkProfile, sdkLevel;
                                     if (mapper && mapper->mapProfile(pl.profile, &sdkProfile)
                                             && mapper->mapLevel(pl.level, &sdkLevel)) {
                                         caps->addProfileLevel(
                                                 (uint32_t)sdkProfile, (uint32_t)sdkLevel);
                                         gotProfileLevels = true;
+                                        added = true;
                                     } else if (!mapper) {
+                                        sdkProfile = pl.profile;
+                                        sdkLevel = pl.level;
                                         caps->addProfileLevel(pl.profile, pl.level);
                                         gotProfileLevels = true;
+                                        added = true;
+                                    }
+                                    if (added && hdrSupported) {
+                                        static ALookup<int32_t, int32_t> sHdrProfileMap = {
+                                            { VP9Profile2, VP9Profile2HDR },
+                                            { VP9Profile3, VP9Profile3HDR },
+                                        };
+                                        int32_t sdkHdrProfile;
+                                        if (sHdrProfileMap.lookup(sdkProfile, &sdkHdrProfile)) {
+                                            caps->addProfileLevel(
+                                                    (uint32_t)sdkHdrProfile, (uint32_t)sdkLevel);
+                                        }
                                     }
 
                                     // for H.263 also advertise the second highest level if the
diff --git a/media/sfplugin/utils/Android.bp b/media/sfplugin/utils/Android.bp
index 0d37529..fb5d9e4 100644
--- a/media/sfplugin/utils/Android.bp
+++ b/media/sfplugin/utils/Android.bp
@@ -26,6 +26,10 @@
         "libutils",
     ],
 
+    static_libs: [
+        "libyuv_static",
+    ],
+
     sanitize: {
         cfi: true,
         misc_undefined: [
diff --git a/media/sfplugin/utils/Codec2BufferUtils.cpp b/media/sfplugin/utils/Codec2BufferUtils.cpp
index b7519da..6b8663f 100644
--- a/media/sfplugin/utils/Codec2BufferUtils.cpp
+++ b/media/sfplugin/utils/Codec2BufferUtils.cpp
@@ -18,6 +18,8 @@
 #define LOG_TAG "Codec2BufferUtils"
 #include <utils/Log.h>
 
+#include <libyuv.h>
+
 #include <list>
 #include <mutex>
 
@@ -62,14 +64,10 @@
  */
 template<bool ToMediaImage, typename View, typename ImagePixel>
 static status_t _ImageCopy(View &view, const MediaImage2 *img, ImagePixel *imgBase) {
-    // TODO: more efficient copying --- e.g. one row at a time, copying
-    //       interleaved planes together, etc.
+    // TODO: more efficient copying --- e.g. copy interleaved planes together, etc.
     const C2PlanarLayout &layout = view.layout();
     const size_t bpp = divUp(img->mBitDepthAllocated, 8u);
-    if (view.width() != img->mWidth
-            || view.height() != img->mHeight) {
-        return BAD_VALUE;
-    }
+
     for (uint32_t i = 0; i < layout.numPlanes; ++i) {
         typename std::conditional<ToMediaImage, uint8_t, const uint8_t>::type *imgRow =
             imgBase + img->mPlane[i].mOffset;
@@ -88,16 +86,30 @@
 
         uint32_t planeW = img->mWidth / plane.colSampling;
         uint32_t planeH = img->mHeight / plane.rowSampling;
-        for (uint32_t row = 0; row < planeH; ++row) {
-            decltype(imgRow) imgPtr = imgRow;
-            decltype(viewRow) viewPtr = viewRow;
-            for (uint32_t col = 0; col < planeW; ++col) {
-                MemCopier<ToMediaImage, 0>::copy(imgPtr, viewPtr, bpp);
-                imgPtr += img->mPlane[i].mColInc;
-                viewPtr += plane.colInc;
+
+        bool canCopyByRow = (plane.colInc == 1) && (img->mPlane[i].mColInc == 1);
+        bool canCopyByPlane = canCopyByRow && (plane.rowInc == img->mPlane[i].mRowInc);
+        if (canCopyByPlane) {
+            MemCopier<ToMediaImage, 0>::copy(imgRow, viewRow, plane.rowInc * planeH);
+        } else if (canCopyByRow) {
+            for (uint32_t row = 0; row < planeH; ++row) {
+                MemCopier<ToMediaImage, 0>::copy(
+                        imgRow, viewRow, std::min(plane.rowInc, img->mPlane[i].mRowInc));
+                imgRow += img->mPlane[i].mRowInc;
+                viewRow += plane.rowInc;
             }
-            imgRow += img->mPlane[i].mRowInc;
-            viewRow += plane.rowInc;
+        } else {
+            for (uint32_t row = 0; row < planeH; ++row) {
+                decltype(imgRow) imgPtr = imgRow;
+                decltype(viewRow) viewPtr = viewRow;
+                for (uint32_t col = 0; col < planeW; ++col) {
+                    MemCopier<ToMediaImage, 0>::copy(imgPtr, viewPtr, bpp);
+                    imgPtr += img->mPlane[i].mColInc;
+                    viewPtr += plane.colInc;
+                }
+                imgRow += img->mPlane[i].mRowInc;
+                viewRow += plane.rowInc;
+            }
         }
     }
     return OK;
@@ -106,10 +118,72 @@
 }  // namespace
 
 status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView &view) {
+    if (view.width() != img->mWidth || view.height() != img->mHeight) {
+        return BAD_VALUE;
+    }
+    if ((IsNV12(view) && IsI420(img)) || (IsI420(view) && IsNV12(img))) {
+        // Take shortcuts to use libyuv functions between NV12 and I420 conversion.
+        const uint8_t* src_y = view.data()[0];
+        const uint8_t* src_u = view.data()[1];
+        const uint8_t* src_v = view.data()[2];
+        int32_t src_stride_y = view.layout().planes[0].rowInc;
+        int32_t src_stride_u = view.layout().planes[1].rowInc;
+        int32_t src_stride_v = view.layout().planes[2].rowInc;
+        uint8_t* dst_y = imgBase + img->mPlane[0].mOffset;
+        uint8_t* dst_u = imgBase + img->mPlane[1].mOffset;
+        uint8_t* dst_v = imgBase + img->mPlane[2].mOffset;
+        int32_t dst_stride_y = img->mPlane[0].mRowInc;
+        int32_t dst_stride_u = img->mPlane[1].mRowInc;
+        int32_t dst_stride_v = img->mPlane[2].mRowInc;
+        if (IsNV12(view) && IsI420(img)) {
+            if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
+                                    dst_u, dst_stride_u, dst_v, dst_stride_v, view.width(),
+                                    view.height())) {
+                return OK;
+            }
+        } else {
+            if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
+                                    dst_y, dst_stride_y, dst_u, dst_stride_u, view.width(),
+                                    view.height())) {
+                return OK;
+            }
+        }
+    }
     return _ImageCopy<true>(view, img, imgBase);
 }
 
 status_t ImageCopy(C2GraphicView &view, const uint8_t *imgBase, const MediaImage2 *img) {
+    if (view.width() != img->mWidth || view.height() != img->mHeight) {
+        return BAD_VALUE;
+    }
+    if ((IsNV12(img) && IsI420(view)) || (IsI420(img) && IsNV12(view))) {
+        // Take shortcuts to use libyuv functions between NV12 and I420 conversion.
+        const uint8_t* src_y = imgBase + img->mPlane[0].mOffset;
+        const uint8_t* src_u = imgBase + img->mPlane[1].mOffset;
+        const uint8_t* src_v = imgBase + img->mPlane[2].mOffset;
+        int32_t src_stride_y = img->mPlane[0].mRowInc;
+        int32_t src_stride_u = img->mPlane[1].mRowInc;
+        int32_t src_stride_v = img->mPlane[2].mRowInc;
+        uint8_t* dst_y = view.data()[0];
+        uint8_t* dst_u = view.data()[1];
+        uint8_t* dst_v = view.data()[2];
+        int32_t dst_stride_y = view.layout().planes[0].rowInc;
+        int32_t dst_stride_u = view.layout().planes[1].rowInc;
+        int32_t dst_stride_v = view.layout().planes[2].rowInc;
+        if (IsNV12(img) && IsI420(view)) {
+            if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
+                                    dst_u, dst_stride_u, dst_v, dst_stride_v, view.width(),
+                                    view.height())) {
+                return OK;
+            }
+        } else {
+            if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
+                                    dst_y, dst_stride_y, dst_u, dst_stride_u, view.width(),
+                                    view.height())) {
+                return OK;
+            }
+        }
+    }
     return _ImageCopy<false>(view, img, imgBase);
 }
 
@@ -137,6 +211,65 @@
             && layout.planes[layout.PLANE_V].rowSampling == 2);
 }
 
+bool IsNV12(const C2GraphicView &view) {
+    if (!IsYUV420(view)) {
+        return false;
+    }
+    const C2PlanarLayout &layout = view.layout();
+    return (layout.rootPlanes == 2
+            && layout.planes[layout.PLANE_U].colInc == 2
+            && layout.planes[layout.PLANE_U].rootIx == layout.PLANE_U
+            && layout.planes[layout.PLANE_U].offset == 0
+            && layout.planes[layout.PLANE_V].colInc == 2
+            && layout.planes[layout.PLANE_V].rootIx == layout.PLANE_U
+            && layout.planes[layout.PLANE_V].offset == 1);
+}
+
+bool IsI420(const C2GraphicView &view) {
+    if (!IsYUV420(view)) {
+        return false;
+    }
+    const C2PlanarLayout &layout = view.layout();
+    return (layout.rootPlanes == 3
+            && layout.planes[layout.PLANE_U].colInc == 1
+            && layout.planes[layout.PLANE_U].rootIx == layout.PLANE_U
+            && layout.planes[layout.PLANE_U].offset == 0
+            && layout.planes[layout.PLANE_V].colInc == 1
+            && layout.planes[layout.PLANE_V].rootIx == layout.PLANE_V
+            && layout.planes[layout.PLANE_V].offset == 0);
+}
+
+bool IsYUV420(const MediaImage2 *img) {
+    return (img->mType == MediaImage2::MEDIA_IMAGE_TYPE_YUV
+            && img->mNumPlanes == 3
+            && img->mBitDepth == 8
+            && img->mBitDepthAllocated == 8
+            && img->mPlane[0].mHorizSubsampling == 1
+            && img->mPlane[0].mVertSubsampling == 1
+            && img->mPlane[1].mHorizSubsampling == 2
+            && img->mPlane[1].mVertSubsampling == 2
+            && img->mPlane[2].mHorizSubsampling == 2
+            && img->mPlane[2].mVertSubsampling == 2);
+}
+
+bool IsNV12(const MediaImage2 *img) {
+    if (!IsYUV420(img)) {
+        return false;
+    }
+    return (img->mPlane[1].mColInc == 2
+            && img->mPlane[2].mColInc == 2
+            && (img->mPlane[2].mOffset - img->mPlane[1].mOffset == 1));
+}
+
+bool IsI420(const MediaImage2 *img) {
+    if (!IsYUV420(img)) {
+        return false;
+    }
+    return (img->mPlane[1].mColInc == 1
+            && img->mPlane[2].mColInc == 1
+            && img->mPlane[2].mOffset > img->mPlane[1].mOffset);
+}
+
 MediaImage2 CreateYUV420PlanarMediaImage2(
         uint32_t width, uint32_t height, uint32_t stride, uint32_t vstride) {
     return MediaImage2 {
diff --git a/media/sfplugin/utils/Codec2BufferUtils.h b/media/sfplugin/utils/Codec2BufferUtils.h
index eaf6776..afadf00 100644
--- a/media/sfplugin/utils/Codec2BufferUtils.h
+++ b/media/sfplugin/utils/Codec2BufferUtils.h
@@ -91,6 +91,31 @@
 bool IsYUV420(const C2GraphicView &view);
 
 /**
+ * Returns true iff a view has a NV12 layout.
+ */
+bool IsNV12(const C2GraphicView &view);
+
+/**
+ * Returns true iff a view has a I420 layout.
+ */
+bool IsI420(const C2GraphicView &view);
+
+/**
+ * Returns true iff a MediaImage2 has a YUV 420 888 layout.
+ */
+bool IsYUV420(const MediaImage2 *img);
+
+/**
+ * Returns true iff a MediaImage2 has a NV12 layout.
+ */
+bool IsNV12(const MediaImage2 *img);
+
+/**
+ * Returns true iff a MediaImage2 has a I420 layout.
+ */
+bool IsI420(const MediaImage2 *img);
+
+/**
  * A raw memory block to use for internal buffers.
  *
  * TODO: replace this with C2LinearBlocks from a private C2BlockPool