/*
 * Copyright 2016, 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 "ACodecBufferChannel"
#include <utils/Log.h>

#include <numeric>

#include <C2Buffer.h>

#include <Codec2BufferUtils.h>

#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android/hardware/drm/1.0/types.h>
#include <binder/MemoryDealer.h>
#include <hidlmemory/FrameworkUtils.h>
#include <media/openmax/OMX_Core.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/MediaCodec.h>
#include <media/MediaCodecBuffer.h>
#include <system/window.h>

#include "include/ACodecBufferChannel.h"
#include "include/SecureBuffer.h"
#include "include/SharedMemoryBuffer.h"

namespace android {
using hardware::fromHeap;
using hardware::hidl_handle;
using hardware::hidl_string;
using hardware::hidl_vec;
using namespace hardware::cas::V1_0;
using namespace hardware::cas::native::V1_0;
using DrmBufferType = hardware::drm::V1_0::BufferType;
using BufferInfo = ACodecBufferChannel::BufferInfo;
using BufferInfoIterator = std::vector<BufferInfo>::const_iterator;

ACodecBufferChannel::~ACodecBufferChannel() {
    if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) {
        mCrypto->unsetHeap(mHeapSeqNum);
    }
}

static BufferInfoIterator findClientBuffer(
        const std::shared_ptr<const std::vector<BufferInfo>> &array,
        const sp<MediaCodecBuffer> &buffer) {
    return std::find_if(
            array->begin(), array->end(),
            [buffer](const BufferInfo &info) { return info.mClientBuffer == buffer; });
}

static BufferInfoIterator findBufferId(
        const std::shared_ptr<const std::vector<BufferInfo>> &array,
        IOMX::buffer_id bufferId) {
    return std::find_if(
            array->begin(), array->end(),
            [bufferId](const BufferInfo &info) { return bufferId == info.mBufferId; });
}

ACodecBufferChannel::BufferInfo::BufferInfo(
        const sp<MediaCodecBuffer> &buffer,
        IOMX::buffer_id bufferId,
        const sp<IMemory> &sharedEncryptedBuffer)
    : mClientBuffer(
          (sharedEncryptedBuffer == nullptr)
          ? buffer
          : new SharedMemoryBuffer(buffer->format(), sharedEncryptedBuffer)),
      mCodecBuffer(buffer),
      mBufferId(bufferId),
      mSharedEncryptedBuffer(sharedEncryptedBuffer) {
}

ACodecBufferChannel::ACodecBufferChannel(
        const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained,
        const sp<AMessage> &pollForRenderedBuffers)
    : mInputBufferFilled(inputBufferFilled),
      mOutputBufferDrained(outputBufferDrained),
      mPollForRenderedBuffers(pollForRenderedBuffers),
      mHeapSeqNum(-1) {
}

status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
    std::shared_ptr<const std::vector<BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
        return -ENOENT;
    }
    if (it->mClientBuffer != it->mCodecBuffer) {
        // Copy metadata from client to codec buffer.
        it->mCodecBuffer->meta()->clear();
        int64_t timeUs;
        CHECK(it->mClientBuffer->meta()->findInt64("timeUs", &timeUs));
        it->mCodecBuffer->meta()->setInt64("timeUs", timeUs);
        int32_t eos;
        if (it->mClientBuffer->meta()->findInt32("eos", &eos)) {
            it->mCodecBuffer->meta()->setInt32("eos", eos);
        }
        int32_t csd;
        if (it->mClientBuffer->meta()->findInt32("csd", &csd)) {
            it->mCodecBuffer->meta()->setInt32("csd", csd);
        }
        int32_t decodeOnly;
        if (it->mClientBuffer->meta()->findInt32("decode-only", &decodeOnly)) {
            it->mCodecBuffer->meta()->setInt32("decode-only", decodeOnly);
        }
    }
    ALOGV("queueInputBuffer #%d", it->mBufferId);
    sp<AMessage> msg = mInputBufferFilled->dup();
    msg->setObject("buffer", it->mCodecBuffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->post();
    return OK;
}

status_t ACodecBufferChannel::queueSecureInputBuffer(
        const sp<MediaCodecBuffer> &buffer, bool secure, const uint8_t *key,
        const uint8_t *iv, CryptoPlugin::Mode mode, CryptoPlugin::Pattern pattern,
        const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
        AString *errorDetailMsg) {
    if (!hasCryptoOrDescrambler() || mDealer == nullptr) {
        return -ENOSYS;
    }
    std::shared_ptr<const std::vector<BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
        return -ENOENT;
    }

    native_handle_t *secureHandle = NULL;
    if (secure) {
        sp<SecureBuffer> secureData =
                static_cast<SecureBuffer *>(it->mCodecBuffer.get());
        if (secureData->getDestinationType() != ICrypto::kDestinationTypeNativeHandle) {
            return BAD_VALUE;
        }
        secureHandle = static_cast<native_handle_t *>(secureData->getDestinationPointer());
    }
    ssize_t result = -1;
    ssize_t codecDataOffset = 0;
    if (numSubSamples == 1
            && subSamples[0].mNumBytesOfClearData == 0
            && subSamples[0].mNumBytesOfEncryptedData == 0) {
        // We don't need to go through crypto or descrambler if the input is empty.
        result = 0;
    } else if (mCrypto != NULL) {
        hardware::drm::V1_0::DestinationBuffer destination;
        if (secure) {
            destination.type = DrmBufferType::NATIVE_HANDLE;
            destination.secureMemory = hidl_handle(secureHandle);
        } else {
            destination.type = DrmBufferType::SHARED_MEMORY;
            IMemoryToSharedBuffer(
                    mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory);
        }

        hardware::drm::V1_0::SharedBuffer source;
        IMemoryToSharedBuffer(it->mSharedEncryptedBuffer, mHeapSeqNum, &source);

        result = mCrypto->decrypt(key, iv, mode, pattern,
                source, it->mClientBuffer->offset(),
                subSamples, numSubSamples, destination, errorDetailMsg);

        if (result < 0) {
            return result;
        }

        if (destination.type == DrmBufferType::SHARED_MEMORY) {
            memcpy(it->mCodecBuffer->base(), mDecryptDestination->unsecurePointer(), result);
        }
    } else {
        // Here we cast CryptoPlugin::SubSample to hardware::cas::native::V1_0::SubSample
        // directly, the structure definitions should match as checked in DescramblerImpl.cpp.
        hidl_vec<SubSample> hidlSubSamples;
        hidlSubSamples.setToExternal((SubSample *)subSamples, numSubSamples, false /*own*/);

        ssize_t offset;
        size_t size;
        it->mSharedEncryptedBuffer->getMemory(&offset, &size);
        hardware::cas::native::V1_0::SharedBuffer srcBuffer = {
                .heapBase = *mHidlMemory,
                .offset = (uint64_t) offset,
                .size = size
        };

        DestinationBuffer dstBuffer;
        if (secure) {
            dstBuffer.type = BufferType::NATIVE_HANDLE;
            dstBuffer.secureMemory = hidl_handle(secureHandle);
        } else {
            dstBuffer.type = BufferType::SHARED_MEMORY;
            dstBuffer.nonsecureMemory = srcBuffer;
        }

        Status status = Status::OK;
        hidl_string detailedError;
        ScramblingControl sctrl = ScramblingControl::UNSCRAMBLED;

        if (key != NULL) {
            sctrl = (ScramblingControl)key[0];
            // Adjust for the PES offset
            codecDataOffset = key[2] | (key[3] << 8);
        }

        auto returnVoid = mDescrambler->descramble(
                sctrl,
                hidlSubSamples,
                srcBuffer,
                0,
                dstBuffer,
                0,
                [&status, &result, &detailedError] (
                        Status _status, uint32_t _bytesWritten,
                        const hidl_string& _detailedError) {
                    status = _status;
                    result = (ssize_t)_bytesWritten;
                    detailedError = _detailedError;
                });

        if (!returnVoid.isOk() || status != Status::OK || result < 0) {
            ALOGE("descramble failed, trans=%s, status=%d, result=%zd",
                    returnVoid.description().c_str(), status, result);
            return UNKNOWN_ERROR;
        }

        if (result < codecDataOffset) {
            ALOGD("invalid codec data offset: %zd, result %zd", codecDataOffset, result);
            return BAD_VALUE;
        }

        ALOGV("descramble succeeded, %zd bytes", result);

        if (dstBuffer.type == BufferType::SHARED_MEMORY) {
            memcpy(it->mCodecBuffer->base(),
                    (uint8_t*)it->mSharedEncryptedBuffer->unsecurePointer(),
                    result);
        }
    }

    it->mCodecBuffer->setRange(codecDataOffset, result - codecDataOffset);

    // Copy metadata from client to codec buffer.
    it->mCodecBuffer->meta()->clear();
    int64_t timeUs;
    CHECK(it->mClientBuffer->meta()->findInt64("timeUs", &timeUs));
    it->mCodecBuffer->meta()->setInt64("timeUs", timeUs);
    int32_t eos;
    if (it->mClientBuffer->meta()->findInt32("eos", &eos)) {
        it->mCodecBuffer->meta()->setInt32("eos", eos);
    }
    int32_t csd;
    if (it->mClientBuffer->meta()->findInt32("csd", &csd)) {
        it->mCodecBuffer->meta()->setInt32("csd", csd);
    }
    int32_t decodeOnly;
    if (it->mClientBuffer->meta()->findInt32("decode-only", &decodeOnly)) {
        it->mCodecBuffer->meta()->setInt32("decode-only", decodeOnly);
    }

    ALOGV("queueSecureInputBuffer #%d", it->mBufferId);
    sp<AMessage> msg = mInputBufferFilled->dup();
    msg->setObject("buffer", it->mCodecBuffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->post();
    return OK;
}

status_t ACodecBufferChannel::attachBuffer(
        const std::shared_ptr<C2Buffer> &c2Buffer,
        const sp<MediaCodecBuffer> &buffer) {
    switch (c2Buffer->data().type()) {
        case C2BufferData::LINEAR: {
            if (c2Buffer->data().linearBlocks().size() != 1u) {
                return -ENOSYS;
            }
            C2ConstLinearBlock block{c2Buffer->data().linearBlocks().front()};
            C2ReadView view{block.map().get()};
            size_t copyLength = std::min(size_t(view.capacity()), buffer->capacity());
            ALOGV_IF(view.capacity() > buffer->capacity(),
                    "view.capacity() = %zu, buffer->capacity() = %zu",
                    view.capacity(), buffer->capacity());
            memcpy(buffer->base(), view.data(), copyLength);
            buffer->setRange(0, copyLength);
            break;
        }
        case C2BufferData::GRAPHIC: {
            sp<ABuffer> imageData;
            if (!buffer->format()->findBuffer("image-data", &imageData)) {
                return -ENOSYS;
            }
            if (c2Buffer->data().graphicBlocks().size() != 1u) {
                return -ENOSYS;
            }
            C2ConstGraphicBlock block{c2Buffer->data().graphicBlocks().front()};
            const C2GraphicView view{block.map().get()};
            status_t err = ImageCopy(
                    buffer->base(), (const MediaImage2 *)(imageData->base()), view);
            if (err != OK) {
                return err;
            }
            break;
        }
        case C2BufferData::LINEAR_CHUNKS:  [[fallthrough]];
        case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]];
        default:
            return -ENOSYS;
    }

    return OK;
}

int32_t ACodecBufferChannel::getHeapSeqNum(const sp<HidlMemory> &memory) {
    CHECK(mCrypto);
    auto it = mHeapSeqNumMap.find(memory);
    int32_t heapSeqNum = -1;
    if (it == mHeapSeqNumMap.end()) {
        heapSeqNum = mCrypto->setHeap(memory);
        mHeapSeqNumMap.emplace(memory, heapSeqNum);
    } else {
        heapSeqNum = it->second;
    }
    return heapSeqNum;
}

status_t ACodecBufferChannel::attachEncryptedBuffer(
        const sp<hardware::HidlMemory> &memory,
        bool secure,
        const uint8_t *key,
        const uint8_t *iv,
        CryptoPlugin::Mode mode,
        CryptoPlugin::Pattern pattern,
        size_t offset,
        const CryptoPlugin::SubSample *subSamples,
        size_t numSubSamples,
        const sp<MediaCodecBuffer> &buffer,
        AString* errorDetailMsg) {
    std::shared_ptr<const std::vector<BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
        return -ENOENT;
    }

    native_handle_t *secureHandle = NULL;
    if (secure) {
        sp<SecureBuffer> secureData =
                static_cast<SecureBuffer *>(it->mCodecBuffer.get());
        if (secureData->getDestinationType() != ICrypto::kDestinationTypeNativeHandle) {
            return BAD_VALUE;
        }
        secureHandle = static_cast<native_handle_t *>(secureData->getDestinationPointer());
    }
    size_t size = 0;
    for (size_t i = 0; i < numSubSamples; ++i) {
        size += subSamples[i].mNumBytesOfClearData + subSamples[i].mNumBytesOfEncryptedData;
    }
    ssize_t result = -1;
    ssize_t codecDataOffset = 0;
    if (mCrypto != NULL) {
        hardware::drm::V1_0::DestinationBuffer destination;
        if (secure) {
            destination.type = DrmBufferType::NATIVE_HANDLE;
            destination.secureMemory = hidl_handle(secureHandle);
        } else {
            destination.type = DrmBufferType::SHARED_MEMORY;
            IMemoryToSharedBuffer(
                    mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory);
        }

        int32_t heapSeqNum = getHeapSeqNum(memory);
        hardware::drm::V1_0::SharedBuffer source{(uint32_t)heapSeqNum, offset, size};

        result = mCrypto->decrypt(key, iv, mode, pattern,
                source, it->mClientBuffer->offset(),
                subSamples, numSubSamples, destination, errorDetailMsg);

        if (result < 0) {
            return result;
        }

        if (destination.type == DrmBufferType::SHARED_MEMORY) {
            memcpy(it->mCodecBuffer->base(), mDecryptDestination->unsecurePointer(), result);
        }
    } else {
        // Here we cast CryptoPlugin::SubSample to hardware::cas::native::V1_0::SubSample
        // directly, the structure definitions should match as checked in DescramblerImpl.cpp.
        hidl_vec<SubSample> hidlSubSamples;
        hidlSubSamples.setToExternal((SubSample *)subSamples, numSubSamples, false /*own*/);

        hardware::cas::native::V1_0::SharedBuffer srcBuffer = {
                .heapBase = *memory,
                .offset = (uint64_t) offset,
                .size = size
        };

        DestinationBuffer dstBuffer;
        if (secure) {
            dstBuffer.type = BufferType::NATIVE_HANDLE;
            dstBuffer.secureMemory = hidl_handle(secureHandle);
        } else {
            dstBuffer.type = BufferType::SHARED_MEMORY;
            dstBuffer.nonsecureMemory = srcBuffer;
        }

        Status status = Status::OK;
        hidl_string detailedError;
        ScramblingControl sctrl = ScramblingControl::UNSCRAMBLED;

        if (key != NULL) {
            sctrl = (ScramblingControl)key[0];
            // Adjust for the PES offset
            codecDataOffset = key[2] | (key[3] << 8);
        }

        auto returnVoid = mDescrambler->descramble(
                sctrl,
                hidlSubSamples,
                srcBuffer,
                0,
                dstBuffer,
                0,
                [&status, &result, &detailedError] (
                        Status _status, uint32_t _bytesWritten,
                        const hidl_string& _detailedError) {
                    status = _status;
                    result = (ssize_t)_bytesWritten;
                    detailedError = _detailedError;
                });
        if (errorDetailMsg) {
            errorDetailMsg->setTo(detailedError.c_str(), detailedError.size());
        }
        if (!returnVoid.isOk() || status != Status::OK || result < 0) {
            ALOGE("descramble failed, trans=%s, status=%d, result=%zd",
                    returnVoid.description().c_str(), status, result);
            return UNKNOWN_ERROR;
        }

        if (result < codecDataOffset) {
            ALOGD("invalid codec data offset: %zd, result %zd", codecDataOffset, result);
            return BAD_VALUE;
        }

        ALOGV("descramble succeeded, %zd bytes", result);

        if (dstBuffer.type == BufferType::SHARED_MEMORY) {
            memcpy(it->mCodecBuffer->base(),
                    (uint8_t*)it->mSharedEncryptedBuffer->unsecurePointer(),
                    result);
        }
    }

    it->mCodecBuffer->setRange(codecDataOffset, result - codecDataOffset);
    return OK;
}

status_t ACodecBufferChannel::renderOutputBuffer(
        const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
    std::shared_ptr<const std::vector<BufferInfo>> array(
            std::atomic_load(&mOutputBuffers));
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
        return -ENOENT;
    }

    ALOGV("renderOutputBuffer #%d", it->mBufferId);
    sp<AMessage> msg = mOutputBufferDrained->dup();
    msg->setObject("buffer", buffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->setInt32("render", true);
    msg->setInt64("timestampNs", timestampNs);
    msg->post();
    return OK;
}

void ACodecBufferChannel::pollForRenderedBuffers() {
    mPollForRenderedBuffers->post();
}

status_t ACodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer) {
    std::shared_ptr<const std::vector<BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    bool input = true;
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
        array = std::atomic_load(&mOutputBuffers);
        input = false;
        it = findClientBuffer(array, buffer);
        if (it == array->end()) {
            return -ENOENT;
        }
    }
    ALOGV("discardBuffer #%d", it->mBufferId);
    sp<AMessage> msg = input ? mInputBufferFilled->dup() : mOutputBufferDrained->dup();
    msg->setObject("buffer", it->mCodecBuffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->setInt32("discarded", true);
    msg->post();
    return OK;
}

void ACodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
    std::shared_ptr<const std::vector<BufferInfo>> inputBuffers(
            std::atomic_load(&mInputBuffers));
    array->clear();
    for (const BufferInfo &elem : *inputBuffers) {
        array->push_back(elem.mClientBuffer);
    }
}

void ACodecBufferChannel::getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
    std::shared_ptr<const std::vector<BufferInfo>> outputBuffers(
            std::atomic_load(&mOutputBuffers));
    array->clear();
    for (const BufferInfo &elem : *outputBuffers) {
        array->push_back(elem.mClientBuffer);
    }
}

sp<MemoryDealer> ACodecBufferChannel::makeMemoryDealer(size_t heapSize) {
    sp<MemoryDealer> dealer;
    if (mDealer != nullptr && mCrypto != nullptr && mHeapSeqNum >= 0) {
        mCrypto->unsetHeap(mHeapSeqNum);
    }
    dealer = new MemoryDealer(heapSize, "ACodecBufferChannel");
    if (mCrypto != nullptr) {
        sp<HidlMemory> hHeap = fromHeap(dealer->getMemoryHeap());
        int32_t seqNum = mCrypto->setHeap(hHeap);
        if (seqNum >= 0) {
            mHeapSeqNum = seqNum;
            ALOGV("setHeap returned mHeapSeqNum=%d", mHeapSeqNum);
        } else {
            mHeapSeqNum = -1;
            ALOGE("setHeap failed, setting mHeapSeqNum=-1");
        }
    } else if (mDescrambler != nullptr) {
        sp<IMemoryHeap> heap = dealer->getMemoryHeap();
        mHidlMemory = fromHeap(heap);
        if (mHidlMemory != NULL) {
            ALOGV("created hidl_memory for descrambler");
        } else {
            ALOGE("failed to create hidl_memory for descrambler");
        }
    }
    return dealer;
}

void ACodecBufferChannel::setInputBufferArray(const std::vector<BufferAndId> &array) {
    if (hasCryptoOrDescrambler()) {
        size_t totalSize = std::accumulate(
                array.begin(), array.end(), 0u,
                [alignment = MemoryDealer::getAllocationAlignment()]
                (size_t sum, const BufferAndId& elem) {
                    return sum + align(elem.mBuffer->capacity(), alignment);
                });
        size_t maxSize = std::accumulate(
                array.begin(), array.end(), 0u,
                [alignment = MemoryDealer::getAllocationAlignment()]
                (size_t max, const BufferAndId& elem) {
                    return std::max(max, align(elem.mBuffer->capacity(), alignment));
                });
        size_t destinationBufferSize = maxSize;
        size_t heapSize = totalSize + destinationBufferSize;
        if (heapSize > 0) {
            mDealer = makeMemoryDealer(heapSize);
            mDecryptDestination = mDealer->allocate(destinationBufferSize);
        }
    }
    std::vector<BufferInfo> inputBuffers;
    for (const BufferAndId &elem : array) {
        sp<IMemory> sharedEncryptedBuffer;
        if (hasCryptoOrDescrambler()) {
            sharedEncryptedBuffer = mDealer->allocate(elem.mBuffer->capacity());
        }
        inputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, sharedEncryptedBuffer);
    }
    std::atomic_store(
            &mInputBuffers,
            std::make_shared<const std::vector<BufferInfo>>(inputBuffers));
}

void ACodecBufferChannel::setOutputBufferArray(const std::vector<BufferAndId> &array) {
    std::vector<BufferInfo> outputBuffers;
    for (const BufferAndId &elem : array) {
        outputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, nullptr);
    }
    std::atomic_store(
            &mOutputBuffers,
            std::make_shared<const std::vector<BufferInfo>>(outputBuffers));
}

void ACodecBufferChannel::fillThisBuffer(IOMX::buffer_id bufferId) {
    ALOGV("fillThisBuffer #%d", bufferId);
    std::shared_ptr<const std::vector<BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findBufferId(array, bufferId);

    if (it == array->end()) {
        ALOGE("fillThisBuffer: unrecognized buffer #%d", bufferId);
        return;
    }
    if (it->mClientBuffer != it->mCodecBuffer) {
        it->mClientBuffer->setFormat(it->mCodecBuffer->format());
    }

    mCallback->onInputBufferAvailable(
            std::distance(array->begin(), it),
            it->mClientBuffer);
}

void ACodecBufferChannel::drainThisBuffer(
        IOMX::buffer_id bufferId,
        OMX_U32 omxFlags) {
    ALOGV("drainThisBuffer #%d", bufferId);
    std::shared_ptr<const std::vector<BufferInfo>> array(
            std::atomic_load(&mOutputBuffers));
    BufferInfoIterator it = findBufferId(array, bufferId);

    if (it == array->end()) {
        ALOGE("drainThisBuffer: unrecognized buffer #%d", bufferId);
        return;
    }
    if (it->mClientBuffer != it->mCodecBuffer) {
        it->mClientBuffer->setFormat(it->mCodecBuffer->format());
    }

    uint32_t flags = 0;
    if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
        flags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
    }
    if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
        flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
    }
    if (omxFlags & OMX_BUFFERFLAG_EOS) {
        flags |= MediaCodec::BUFFER_FLAG_EOS;
    }
    if (omxFlags & OMX_BUFFERFLAG_DECODEONLY) {
        flags |= MediaCodec::BUFFER_FLAG_DECODE_ONLY;
    }
    it->mClientBuffer->meta()->setInt32("flags", flags);

    mCallback->onOutputBufferAvailable(
            std::distance(array->begin(), it),
            it->mClientBuffer);
}

void ACodecBufferChannel::setCrypto(const sp<ICrypto> &crypto) {
    if (mCrypto != nullptr) {
        for (std::pair<wp<HidlMemory>, int32_t> entry : mHeapSeqNumMap) {
            mCrypto->unsetHeap(entry.second);
        }
        mHeapSeqNumMap.clear();
        if (mHeapSeqNum >= 0) {
            mCrypto->unsetHeap(mHeapSeqNum);
            mHeapSeqNum = -1;
        }
    }
    mCrypto = crypto;
}

void ACodecBufferChannel::setDescrambler(const sp<IDescrambler> &descrambler) {
    mDescrambler = descrambler;
}

}  // namespace android
