C++程序  |  1391行  |  49.87 KB

/*
 * Copyright (C) 2018 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 "Codec2Client"
#include <log/log.h>

#include <codec2/hidl/client.h>

#include <deque>
#include <limits>
#include <map>
#include <type_traits>
#include <vector>

#include <android-base/properties.h>
#include <bufferpool/ClientManager.h>
#include <cutils/native_handle.h>
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
#include <hidl/HidlSupport.h>
#undef LOG

#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
#include <hardware/google/media/c2/1.0/IComponent.h>
#include <hardware/google/media/c2/1.0/IComponentInterface.h>
#include <hardware/google/media/c2/1.0/IComponentListener.h>
#include <hardware/google/media/c2/1.0/IComponentStore.h>
#include <hardware/google/media/c2/1.0/IConfigurable.h>

#include <C2Debug.h>
#include <C2BufferPriv.h>
#include <C2PlatformSupport.h>

namespace android {

using ::android::hardware::hidl_vec;
using ::android::hardware::hidl_string;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::TWGraphicBufferProducer;

using namespace ::hardware::google::media::c2::V1_0;
using namespace ::hardware::google::media::c2::V1_0::utils;
using namespace ::android::hardware::media::bufferpool::V1_0;
using namespace ::android::hardware::media::bufferpool::V1_0::implementation;

namespace /* unnamed */ {

// c2_status_t value that corresponds to hwbinder transaction failure.
constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;

// List of known IComponentStore services in the decreasing order of preference.
constexpr const char* kClientNames[] = {
        "default",
        "software",
    };

// Number of known IComponentStore services.
constexpr size_t kNumClients = std::extent<decltype(kClientNames)>::value;

typedef std::array<std::shared_ptr<Codec2Client>, kNumClients> ClientList;

// Convenience methods to obtain known clients.
std::shared_ptr<Codec2Client> getClient(size_t index) {
    return Codec2Client::CreateFromService(kClientNames[index]);
}

ClientList getClientList() {
    ClientList list;
    for (size_t i = 0; i < list.size(); ++i) {
        list[i] = getClient(i);
    }
    return list;
}

} // unnamed

// Codec2ConfigurableClient

const C2String& Codec2ConfigurableClient::getName() const {
    return mName;
}

Codec2ConfigurableClient::Base* Codec2ConfigurableClient::base() const {
    return static_cast<Base*>(mBase.get());
}

Codec2ConfigurableClient::Codec2ConfigurableClient(
        const sp<Codec2ConfigurableClient::Base>& base) : mBase(base) {
    Return<void> transStatus = base->getName(
            [this](const hidl_string& name) {
                mName = name.c_str();
            });
    if (!transStatus.isOk()) {
        ALOGE("Cannot obtain name from IConfigurable.");
    }
}

c2_status_t Codec2ConfigurableClient::query(
        const std::vector<C2Param*> &stackParams,
        const std::vector<C2Param::Index> &heapParamIndices,
        c2_blocking_t mayBlock,
        std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
    hidl_vec<ParamIndex> indices(
            stackParams.size() + heapParamIndices.size());
    size_t numIndices = 0;
    for (C2Param* const& stackParam : stackParams) {
        if (!stackParam) {
            ALOGW("query -- null stack param encountered.");
            continue;
        }
        indices[numIndices++] = static_cast<ParamIndex>(stackParam->index());
    }
    size_t numStackIndices = numIndices;
    for (const C2Param::Index& index : heapParamIndices) {
        indices[numIndices++] =
                static_cast<ParamIndex>(static_cast<uint32_t>(index));
    }
    indices.resize(numIndices);
    if (heapParams) {
        heapParams->reserve(heapParams->size() + numIndices);
    }
    c2_status_t status;
    Return<void> transStatus = base()->query(
            indices,
            mayBlock == C2_MAY_BLOCK,
            [&status, &numStackIndices, &stackParams, heapParams](
                    Status s, const Params& p) {
                status = static_cast<c2_status_t>(s);
                if (status != C2_OK && status != C2_BAD_INDEX) {
                    ALOGE("query -- call failed. "
                            "Error code = %d", static_cast<int>(status));
                    return;
                }
                std::vector<C2Param*> paramPointers;
                c2_status_t parseStatus = parseParamsBlob(&paramPointers, p);
                if (parseStatus != C2_OK) {
                    ALOGE("query -- error while parsing params. "
                            "Error code = %d", static_cast<int>(status));
                    status = parseStatus;
                    return;
                }
                size_t i = 0;
                for (auto it = paramPointers.begin(); it != paramPointers.end(); ) {
                    C2Param* paramPointer = *it;
                    if (numStackIndices > 0) {
                        --numStackIndices;
                        if (!paramPointer) {
                            ALOGW("query -- null stack param.");
                            ++it;
                            continue;
                        }
                        for (; i < stackParams.size() && !stackParams[i]; ) {
                            ++i;
                        }
                        if (i >= stackParams.size()) {
                            ALOGE("query -- unexpected error.");
                            status = C2_CORRUPTED;
                            return;
                        }
                        if (stackParams[i]->index() != paramPointer->index()) {
                            ALOGW("query -- param skipped. index = %d",
                                    static_cast<int>(stackParams[i]->index()));
                            stackParams[i++]->invalidate();
                            continue;
                        }
                        if (!stackParams[i++]->updateFrom(*paramPointer)) {
                            ALOGW("query -- param update failed. index = %d",
                                    static_cast<int>(paramPointer->index()));
                        }
                    } else {
                        if (!paramPointer) {
                            ALOGW("query -- null heap param.");
                            ++it;
                            continue;
                        }
                        if (!heapParams) {
                            ALOGW("query -- unexpected extra stack param.");
                        } else {
                            heapParams->emplace_back(C2Param::Copy(*paramPointer));
                        }
                    }
                    ++it;
                }
            });
    if (!transStatus.isOk()) {
        ALOGE("query -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    return status;
}

c2_status_t Codec2ConfigurableClient::config(
        const std::vector<C2Param*> &params,
        c2_blocking_t mayBlock,
        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
    Params hidlParams;
    Status hidlStatus = createParamsBlob(&hidlParams, params);
    if (hidlStatus != Status::OK) {
        ALOGE("config -- bad input.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status;
    Return<void> transStatus = base()->config(
            hidlParams,
            mayBlock == C2_MAY_BLOCK,
            [&status, &params, failures](
                    Status s,
                    const hidl_vec<SettingResult> f,
                    const Params& o) {
                status = static_cast<c2_status_t>(s);
                if (status != C2_OK) {
                    ALOGD("config -- call failed. "
                            "Error code = %d", static_cast<int>(status));
                }
                size_t i = failures->size();
                failures->resize(i + f.size());
                for (const SettingResult& sf : f) {
                    status = objcpy(&(*failures)[i++], sf);
                    if (status != C2_OK) {
                        ALOGE("config -- invalid returned SettingResult. "
                                "Error code = %d", static_cast<int>(status));
                        return;
                    }
                }
                status = updateParamsFromBlob(params, o);
            });
    if (!transStatus.isOk()) {
        ALOGE("config -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    return status;
}

c2_status_t Codec2ConfigurableClient::querySupportedParams(
        std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const {
    // TODO: Cache and query properly!
    c2_status_t status;
    Return<void> transStatus = base()->querySupportedParams(
            std::numeric_limits<uint32_t>::min(),
            std::numeric_limits<uint32_t>::max(),
            [&status, params](
                    Status s,
                    const hidl_vec<ParamDescriptor>& p) {
                status = static_cast<c2_status_t>(s);
                if (status != C2_OK) {
                    ALOGE("querySupportedParams -- call failed. "
                            "Error code = %d", static_cast<int>(status));
                    return;
                }
                size_t i = params->size();
                params->resize(i + p.size());
                for (const ParamDescriptor& sp : p) {
                    status = objcpy(&(*params)[i++], sp);
                    if (status != C2_OK) {
                        ALOGE("querySupportedParams -- "
                                "invalid returned ParamDescriptor. "
                                "Error code = %d", static_cast<int>(status));
                        return;
                    }
                }
            });
    if (!transStatus.isOk()) {
        ALOGE("querySupportedParams -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    return status;
}

c2_status_t Codec2ConfigurableClient::querySupportedValues(
        std::vector<C2FieldSupportedValuesQuery>& fields,
        c2_blocking_t mayBlock) const {
    hidl_vec<FieldSupportedValuesQuery> inFields(fields.size());
    for (size_t i = 0; i < fields.size(); ++i) {
        Status hidlStatus = objcpy(&inFields[i], fields[i]);
        if (hidlStatus != Status::OK) {
            ALOGE("querySupportedValues -- bad input");
            return C2_TRANSACTION_FAILED;
        }
    }

    c2_status_t status;
    Return<void> transStatus = base()->querySupportedValues(
            inFields,
            mayBlock == C2_MAY_BLOCK,
            [&status, &inFields, &fields](
                    Status s,
                    const hidl_vec<FieldSupportedValuesQueryResult>& r) {
                status = static_cast<c2_status_t>(s);
                if (status != C2_OK) {
                    ALOGE("querySupportedValues -- call failed. "
                            "Error code = %d", static_cast<int>(status));
                    return;
                }
                if (r.size() != fields.size()) {
                    ALOGE("querySupportedValues -- input and output lists "
                            "have different sizes.");
                    status = C2_CORRUPTED;
                    return;
                }
                for (size_t i = 0; i < fields.size(); ++i) {
                    status = objcpy(&fields[i], inFields[i], r[i]);
                    if (status != C2_OK) {
                        ALOGE("querySupportedValues -- invalid returned value. "
                                "Error code = %d", static_cast<int>(status));
                        return;
                    }
                }
            });
    if (!transStatus.isOk()) {
        ALOGE("querySupportedValues -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    return status;
}

// Codec2Client::Component::HidlListener
struct Codec2Client::Component::HidlListener : public IComponentListener {
    std::weak_ptr<Component> component;
    std::weak_ptr<Listener> base;

    virtual Return<void> onWorkDone(const WorkBundle& workBundle) override {
        std::list<std::unique_ptr<C2Work>> workItems;
        c2_status_t status = objcpy(&workItems, workBundle);
        if (status != C2_OK) {
            ALOGI("onWorkDone -- received corrupted WorkBundle. "
                    "status = %d.", static_cast<int>(status));
            return Void();
        }
        // release input buffers potentially held by the component from queue
        size_t numDiscardedInputBuffers = 0;
        std::shared_ptr<Codec2Client::Component> strongComponent = component.lock();
        if (strongComponent) {
            numDiscardedInputBuffers = strongComponent->handleOnWorkDone(workItems);
        }
        if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) {
            listener->onWorkDone(component, workItems, numDiscardedInputBuffers);
        } else {
            ALOGD("onWorkDone -- listener died.");
        }
        return Void();
    }

    virtual Return<void> onTripped(
            const hidl_vec<SettingResult>& settingResults) override {
        std::vector<std::shared_ptr<C2SettingResult>> c2SettingResults(
                settingResults.size());
        c2_status_t status;
        for (size_t i = 0; i < settingResults.size(); ++i) {
            std::unique_ptr<C2SettingResult> c2SettingResult;
            status = objcpy(&c2SettingResult, settingResults[i]);
            if (status != C2_OK) {
                ALOGI("onTripped -- received corrupted SettingResult. "
                        "status = %d.", static_cast<int>(status));
                return Void();
            }
            c2SettingResults[i] = std::move(c2SettingResult);
        }
        if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) {
            listener->onTripped(component, c2SettingResults);
        } else {
            ALOGD("onTripped -- listener died.");
        }
        return Void();
    }

    virtual Return<void> onError(Status s, uint32_t errorCode) override {
        ALOGD("onError -- status = %d, errorCode = %u.",
                static_cast<int>(s),
                static_cast<unsigned>(errorCode));
        if (std::shared_ptr<Listener> listener = base.lock()) {
            listener->onError(component, s == Status::OK ?
                    errorCode : static_cast<c2_status_t>(s));
        } else {
            ALOGD("onError -- listener died.");
        }
        return Void();
    }

    virtual Return<void> onFramesRendered(
            const hidl_vec<RenderedFrame>& renderedFrames) override {
        std::shared_ptr<Listener> listener = base.lock();
        std::vector<Codec2Client::Listener::RenderedFrame> rfs;
        rfs.reserve(renderedFrames.size());
        for (const RenderedFrame& rf : renderedFrames) {
            if (rf.slotId >= 0) {
                if (listener) {
                    rfs.emplace_back(rf.bufferQueueId,
                                     rf.slotId,
                                     rf.timestampNs);
                }
            } else {
                std::shared_ptr<Codec2Client::Component> strongComponent =
                        component.lock();
                if (strongComponent) {
                    uint64_t frameIndex = rf.bufferQueueId;
                    size_t bufferIndex = static_cast<size_t>(~rf.slotId);
                    ALOGV("Received death notification of input buffer: "
                          "frameIndex = %llu, bufferIndex = %zu.",
                          static_cast<long long unsigned>(frameIndex),
                          bufferIndex);
                    std::shared_ptr<C2Buffer> buffer =
                            strongComponent->freeInputBuffer(
                                frameIndex, bufferIndex);
                    if (buffer) {
                        listener->onInputBufferDone(buffer);
                    }
                }
            }
        }
        if (!rfs.empty()) {
            if (listener) {
                listener->onFramesRendered(rfs);
            } else {
                ALOGD("onFramesRendered -- listener died.");
            }
        }
        return Void();
    }
};

// Codec2Client
Codec2Client::Base* Codec2Client::base() const {
    return static_cast<Base*>(mBase.get());
}

Codec2Client::Codec2Client(const sp<Codec2Client::Base>& base, std::string instanceName) :
    Codec2ConfigurableClient(base), mListed(false), mInstanceName(instanceName) {
    Return<sp<IClientManager>> transResult = base->getPoolClientManager();
    if (!transResult.isOk()) {
        ALOGE("getPoolClientManager -- failed transaction.");
    } else {
        mHostPoolManager = static_cast<sp<IClientManager>>(transResult);
    }
}

c2_status_t Codec2Client::createComponent(
        const C2String& name,
        const std::shared_ptr<Codec2Client::Listener>& listener,
        std::shared_ptr<Codec2Client::Component>* const component) {

    // TODO: Add support for Bufferpool


    c2_status_t status;
    sp<Component::HidlListener> hidlListener = new Component::HidlListener();
    hidlListener->base = listener;
    Return<void> transStatus = base()->createComponent(
            name,
            hidlListener,
            ClientManager::getInstance(),
            [&status, component, hidlListener](
                    Status s,
                    const sp<IComponent>& c) {
                status = static_cast<c2_status_t>(s);
                if (status != C2_OK) {
                    return;
                }
                *component = std::make_shared<Codec2Client::Component>(c);
                hidlListener->component = *component;
            });
    if (!transStatus.isOk()) {
        ALOGE("createComponent -- failed transaction.");
        return C2_TRANSACTION_FAILED;
    }

    if (status != C2_OK) {
        return status;
    }

    if (!*component) {
        ALOGE("createComponent -- null component.");
        return C2_CORRUPTED;
    }

    status = (*component)->setDeathListener(*component, listener);
    if (status != C2_OK) {
        ALOGE("createComponent -- setDeathListener returned error: %d.",
                static_cast<int>(status));
    }

    (*component)->mBufferPoolSender.setReceiver(mHostPoolManager);
    return status;
}

c2_status_t Codec2Client::createInterface(
        const C2String& name,
        std::shared_ptr<Codec2Client::Interface>* const interface) {
    c2_status_t status;
    Return<void> transStatus = base()->createInterface(
            name,
            [&status, interface](
                    Status s,
                    const sp<IComponentInterface>& i) {
                status = static_cast<c2_status_t>(s);
                if (status != C2_OK) {
                    ALOGE("createInterface -- call failed. "
                            "Error code = %d", static_cast<int>(status));
                    return;
                }
                *interface = std::make_shared<Codec2Client::Interface>(i);
            });
    if (!transStatus.isOk()) {
        ALOGE("createInterface -- failed transaction.");
        return C2_TRANSACTION_FAILED;
    }
    return status;
}

c2_status_t Codec2Client::createInputSurface(
        std::shared_ptr<Codec2Client::InputSurface>* const inputSurface) {
    Return<sp<IInputSurface>> transResult = base()->createInputSurface();
    if (!transResult.isOk()) {
        ALOGE("createInputSurface -- failed transaction.");
        return C2_TRANSACTION_FAILED;
    }
    sp<IInputSurface> result = static_cast<sp<IInputSurface>>(transResult);
    if (!result) {
        *inputSurface = nullptr;
        return C2_OK;
    }
    *inputSurface = std::make_shared<InputSurface>(result);
    if (!*inputSurface) {
        ALOGE("createInputSurface -- unknown error.");
        return C2_CORRUPTED;
    }
    return C2_OK;
}

const std::vector<C2Component::Traits>& Codec2Client::listComponents() const {
    std::lock_guard<std::mutex> lock(mMutex);
    if (mListed) {
        return mTraitsList;
    }
    Return<void> transStatus = base()->listComponents(
            [this](const hidl_vec<IComponentStore::ComponentTraits>& t) {
                mTraitsList.resize(t.size());
                mAliasesBuffer.resize(t.size());
                for (size_t i = 0; i < t.size(); ++i) {
                    c2_status_t status = objcpy(
                            &mTraitsList[i], &mAliasesBuffer[i], t[i]);
                    mTraitsList[i].owner = mInstanceName;
                    if (status != C2_OK) {
                        ALOGE("listComponents -- corrupted output.");
                        return;
                    }
                }
            });
    if (!transStatus.isOk()) {
        ALOGE("listComponents -- failed transaction.");
    }
    mListed = true;
    return mTraitsList;
}

c2_status_t Codec2Client::copyBuffer(
        const std::shared_ptr<C2Buffer>& src,
        const std::shared_ptr<C2Buffer>& dst) {
    // TODO: Implement?
    (void)src;
    (void)dst;
    ALOGE("copyBuffer not implemented");
    return C2_OMITTED;
}

std::shared_ptr<C2ParamReflector>
        Codec2Client::getParamReflector() {
    // TODO: this is not meant to be exposed as C2ParamReflector on the client side; instead, it
    // should reflect the HAL API.
    struct SimpleParamReflector : public C2ParamReflector {
        virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) const {
            hidl_vec<ParamIndex> indices(1);
            indices[0] = static_cast<ParamIndex>(coreIndex.coreIndex());
            std::unique_ptr<C2StructDescriptor> descriptor;
            Return<void> transStatus = mBase->getStructDescriptors(
                    indices,
                    [&descriptor](
                            Status s,
                            const hidl_vec<StructDescriptor>& sd) {
                        c2_status_t status = static_cast<c2_status_t>(s);
                        if (status != C2_OK) {
                            ALOGE("getStructDescriptors -- call failed. "
                                    "Error code = %d", static_cast<int>(status));
                            descriptor.reset();
                            return;
                        }
                        if (sd.size() != 1) {
                            ALOGD("getStructDescriptors -- returned vector of size %zu.",
                                    sd.size());
                            descriptor.reset();
                            return;
                        }
                        status = objcpy(&descriptor, sd[0]);
                        if (status != C2_OK) {
                            ALOGD("getStructDescriptors -- failed to convert. "
                                    "Error code = %d", static_cast<int>(status));
                            descriptor.reset();
                            return;
                        }
                    });
            return descriptor;
        }

        SimpleParamReflector(sp<Base> base)
            : mBase(base) { }

        sp<Base> mBase;
    };

    return std::make_shared<SimpleParamReflector>(base());
};

std::shared_ptr<Codec2Client> Codec2Client::CreateFromService(
        const char* instanceName, bool waitForService) {
    if (!instanceName) {
        return nullptr;
    }
    sp<Base> baseStore = waitForService ?
            Base::getService(instanceName) :
            Base::tryGetService(instanceName);
    if (!baseStore) {
        if (waitForService) {
            ALOGE("Codec2.0 service inaccessible. Check the device manifest.");
        } else {
            ALOGW("Codec2.0 service not available right now. Try again later.");
        }
        return nullptr;
    }
    return std::make_shared<Codec2Client>(baseStore, instanceName);
}

c2_status_t Codec2Client::ForAllStores(
        const std::string &key,
        std::function<c2_status_t(const std::shared_ptr<Codec2Client>&)> predicate) {
    c2_status_t status = C2_NO_INIT;  // no IComponentStores present

    // Cache the mapping key -> index of Codec2Client in getClient().
    static std::mutex key2IndexMutex;
    static std::map<std::string, size_t> key2Index;

    // By default try all stores. However, try the last known client first. If the last known
    // client fails, retry once. We do this by pushing the last known client in front of the
    // list of all clients.
    std::deque<size_t> indices;
    for (size_t index = kNumClients; index > 0; ) {
        indices.push_front(--index);
    }

    bool wasMapped = false;
    std::unique_lock<std::mutex> lock(key2IndexMutex);
    auto it = key2Index.find(key);
    if (it != key2Index.end()) {
        indices.push_front(it->second);
        wasMapped = true;
    }
    lock.unlock();

    for (size_t index : indices) {
        std::shared_ptr<Codec2Client> client = getClient(index);
        if (client) {
            status = predicate(client);
            if (status == C2_OK) {
                lock.lock();
                key2Index[key] = index; // update last known client index
                return status;
            }
        }
        if (wasMapped) {
            ALOGI("Could not find '%s' in last instance. Retrying...", key.c_str());
            wasMapped = false;
        }
    }
    return status;  // return the last status from a valid client
}

std::shared_ptr<Codec2Client::Component>
        Codec2Client::CreateComponentByName(
        const char* componentName,
        const std::shared_ptr<Listener>& listener,
        std::shared_ptr<Codec2Client>* owner) {
    std::shared_ptr<Component> component;
    c2_status_t status = ForAllStores(
            componentName,
            [owner, &component, componentName, &listener](
                    const std::shared_ptr<Codec2Client> &client) -> c2_status_t {
                c2_status_t status = client->createComponent(componentName, listener, &component);
                if (status == C2_OK) {
                    if (owner) {
                        *owner = client;
                    }
                } else if (status != C2_NOT_FOUND) {
                    ALOGD("IComponentStore(%s)::createComponent('%s') returned %s",
                            client->getInstanceName().c_str(), componentName, asString(status));
                }
                return status;
            });
    if (status != C2_OK) {
        ALOGI("Could not create component '%s' (%s)", componentName, asString(status));
    }
    return component;
}

std::shared_ptr<Codec2Client::Interface>
        Codec2Client::CreateInterfaceByName(
        const char* interfaceName,
        std::shared_ptr<Codec2Client>* owner) {
    std::shared_ptr<Interface> interface;
    c2_status_t status = ForAllStores(
            interfaceName,
            [owner, &interface, interfaceName](
                    const std::shared_ptr<Codec2Client> &client) -> c2_status_t {
                c2_status_t status = client->createInterface(interfaceName, &interface);
                if (status == C2_OK) {
                    if (owner) {
                        *owner = client;
                    }
                } else if (status != C2_NOT_FOUND) {
                    ALOGD("IComponentStore(%s)::createInterface('%s') returned %s",
                            client->getInstanceName().c_str(), interfaceName, asString(status));
                }
                return status;
            });
    if (status != C2_OK) {
        ALOGI("Could not create interface '%s' (%s)", interfaceName, asString(status));
    }
    return interface;
}

std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface() {
    uint32_t serviceMask = ::android::base::GetUintProperty(
            "debug.stagefright.c2inputsurface", uint32_t(0));
    for (size_t i = 0; i < kNumClients; ++i) {
        if ((1 << i) & serviceMask) {
            std::shared_ptr<Codec2Client> client = getClient(i);
            std::shared_ptr<Codec2Client::InputSurface> inputSurface;
            if (client &&
                    client->createInputSurface(&inputSurface) == C2_OK &&
                    inputSurface) {
                return inputSurface;
            }
        }
    }
    ALOGW("Could not create an input surface from any Codec2.0 services.");
    return nullptr;
}

const std::vector<C2Component::Traits>& Codec2Client::ListComponents() {
    static std::vector<C2Component::Traits> traitsList = [](){
        std::vector<C2Component::Traits> list;
        size_t listSize = 0;
        ClientList clientList = getClientList();
        for (const std::shared_ptr<Codec2Client>& client : clientList) {
            if (!client) {
                continue;
            }
            listSize += client->listComponents().size();
        }
        list.reserve(listSize);
        for (const std::shared_ptr<Codec2Client>& client : clientList) {
            if (!client) {
                continue;
            }
            list.insert(
                    list.end(),
                    client->listComponents().begin(),
                    client->listComponents().end());
        }
        return list;
    }();

    return traitsList;
}

// Codec2Client::Listener

Codec2Client::Listener::~Listener() {
}

// Codec2Client::Component

Codec2Client::Component::Base* Codec2Client::Component::base() const {
    return static_cast<Base*>(mBase.get());
}

Codec2Client::Component::Component(const sp<Codec2Client::Component::Base>& base) :
    Codec2Client::Configurable(base),
    mBufferPoolSender(nullptr) {
}

Codec2Client::Component::~Component() {
}

c2_status_t Codec2Client::Component::createBlockPool(
        C2Allocator::id_t id,
        C2BlockPool::local_id_t* blockPoolId,
        std::shared_ptr<Codec2Client::Configurable>* configurable) {
    c2_status_t status;
    Return<void> transStatus = base()->createBlockPool(
            static_cast<uint32_t>(id),
            [&status, blockPoolId, configurable](
                    Status s,
                    uint64_t pId,
                    const sp<IConfigurable>& c) {
                status = static_cast<c2_status_t>(s);
                configurable->reset();
                if (status != C2_OK) {
                    ALOGE("createBlockPool -- call failed. "
                            "Error code = %d", static_cast<int>(status));
                    return;
                }
                *blockPoolId = static_cast<C2BlockPool::local_id_t>(pId);
                *configurable = std::make_shared<Codec2Client::Configurable>(c);
            });
    if (!transStatus.isOk()) {
        ALOGE("createBlockPool -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    return status;
}

c2_status_t Codec2Client::Component::destroyBlockPool(
        C2BlockPool::local_id_t localId) {
    Return<Status> transResult = base()->destroyBlockPool(
            static_cast<uint64_t>(localId));
    if (!transResult.isOk()) {
        ALOGE("destroyBlockPool -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    return static_cast<c2_status_t>(static_cast<Status>(transResult));
}

size_t Codec2Client::Component::handleOnWorkDone(
        const std::list<std::unique_ptr<C2Work>> &workItems) {
    // Input buffers' lifetime management
    std::vector<uint64_t> inputDone;
    for (const std::unique_ptr<C2Work> &work : workItems) {
        if (work) {
            if (work->worklets.empty()
                    || !work->worklets.back()
                    || (work->worklets.back()->output.flags & C2FrameData::FLAG_INCOMPLETE) == 0) {
                // input is complete
                inputDone.emplace_back(work->input.ordinal.frameIndex.peeku());
            }
        }
    }

    size_t numDiscardedInputBuffers = 0;
    {
        std::lock_guard<std::mutex> lock(mInputBuffersMutex);
        for (uint64_t inputIndex : inputDone) {
            auto it = mInputBuffers.find(inputIndex);
            if (it == mInputBuffers.end()) {
                ALOGV("onWorkDone -- returned consumed/unknown "
                      "input frame: index %llu",
                        (long long)inputIndex);
            } else {
                ALOGV("onWorkDone -- processed input frame: "
                      "index %llu (containing %zu buffers)",
                        (long long)inputIndex, it->second.size());
                mInputBuffers.erase(it);
                mInputBufferCount.erase(inputIndex);
                ++numDiscardedInputBuffers;
            }
        }
    }

    // Output bufferqueue-based blocks' lifetime management
    mOutputBufferQueueMutex.lock();
    sp<IGraphicBufferProducer> igbp = mOutputIgbp;
    uint64_t bqId = mOutputBqId;
    uint32_t generation = mOutputGeneration;
    mOutputBufferQueueMutex.unlock();

    if (igbp) {
        holdBufferQueueBlocks(workItems, igbp, bqId, generation);
    }
    return numDiscardedInputBuffers;
}

std::shared_ptr<C2Buffer> Codec2Client::Component::freeInputBuffer(
        uint64_t frameIndex,
        size_t bufferIndex) {
    std::shared_ptr<C2Buffer> buffer;
    std::lock_guard<std::mutex> lock(mInputBuffersMutex);
    auto it = mInputBuffers.find(frameIndex);
    if (it == mInputBuffers.end()) {
        ALOGI("freeInputBuffer -- Unrecognized input frame index %llu.",
              static_cast<long long unsigned>(frameIndex));
        return nullptr;
    }
    if (bufferIndex >= it->second.size()) {
        ALOGI("freeInputBuffer -- Input buffer no. %zu is invalid in "
              "input frame index %llu.",
              bufferIndex, static_cast<long long unsigned>(frameIndex));
        return nullptr;
    }
    buffer = it->second[bufferIndex];
    if (!buffer) {
        ALOGI("freeInputBuffer -- Input buffer no. %zu in "
              "input frame index %llu has already been freed.",
              bufferIndex, static_cast<long long unsigned>(frameIndex));
        return nullptr;
    }
    it->second[bufferIndex] = nullptr;
    if (--mInputBufferCount[frameIndex] == 0) {
        mInputBuffers.erase(it);
        mInputBufferCount.erase(frameIndex);
    }
    return buffer;
}

c2_status_t Codec2Client::Component::queue(
        std::list<std::unique_ptr<C2Work>>* const items) {
    // remember input buffers queued to hold reference to them
    {
        std::lock_guard<std::mutex> lock(mInputBuffersMutex);
        for (const std::unique_ptr<C2Work> &work : *items) {
            if (!work) {
                continue;
            }
            if (work->input.buffers.size() == 0) {
                continue;
            }

            uint64_t inputIndex = work->input.ordinal.frameIndex.peeku();
            auto res = mInputBuffers.emplace(inputIndex, work->input.buffers);
            if (!res.second) {
                // TODO: append? - for now we are replacing
                res.first->second = work->input.buffers;
                ALOGI("queue -- duplicate input frame: index %llu. "
                      "Discarding the old input frame...",
                        (long long)inputIndex);
            }
            mInputBufferCount[inputIndex] = work->input.buffers.size();
            ALOGV("queue -- queueing input frame: "
                  "index %llu (containing %zu buffers)",
                    (long long)inputIndex, work->input.buffers.size());
        }
    }

    WorkBundle workBundle;
    Status hidlStatus = objcpy(&workBundle, *items, &mBufferPoolSender);
    if (hidlStatus != Status::OK) {
        ALOGE("queue -- bad input.");
        return C2_TRANSACTION_FAILED;
    }
    Return<Status> transStatus = base()->queue(workBundle);
    if (!transStatus.isOk()) {
        ALOGE("queue -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("queue -- call failed. "
                "Error code = %d", static_cast<int>(status));
    }
    return status;
}

c2_status_t Codec2Client::Component::flush(
        C2Component::flush_mode_t mode,
        std::list<std::unique_ptr<C2Work>>* const flushedWork) {
    (void)mode; // Flush mode isn't supported in HIDL yet.
    c2_status_t status;
    Return<void> transStatus = base()->flush(
            [&status, flushedWork](
                    Status s, const WorkBundle& wb) {
                status = static_cast<c2_status_t>(s);
                if (status != C2_OK) {
                    ALOGE("flush -- call failed. "
                            "Error code = %d", static_cast<int>(status));
                    return;
                }
                status = objcpy(flushedWork, wb);
            });
    if (!transStatus.isOk()) {
        ALOGE("flush -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }

    // Indices of flushed work items.
    std::vector<uint64_t> flushedIndices;
    for (const std::unique_ptr<C2Work> &work : *flushedWork) {
        if (work) {
            if (work->worklets.empty()
                    || !work->worklets.back()
                    || (work->worklets.back()->output.flags &
                        C2FrameData::FLAG_INCOMPLETE) == 0) {
                // input is complete
                flushedIndices.emplace_back(
                        work->input.ordinal.frameIndex.peeku());
            }
        }
    }

    // Input buffers' lifetime management
    for (uint64_t flushedIndex : flushedIndices) {
        std::lock_guard<std::mutex> lock(mInputBuffersMutex);
        auto it = mInputBuffers.find(flushedIndex);
        if (it == mInputBuffers.end()) {
            ALOGV("flush -- returned consumed/unknown input frame: "
                  "index %llu",
                    (long long)flushedIndex);
        } else {
            ALOGV("flush -- returned unprocessed input frame: "
                  "index %llu (containing %zu buffers)",
                    (long long)flushedIndex, mInputBufferCount[flushedIndex]);
            mInputBuffers.erase(it);
            mInputBufferCount.erase(flushedIndex);
        }
    }

    // Output bufferqueue-based blocks' lifetime management
    mOutputBufferQueueMutex.lock();
    sp<IGraphicBufferProducer> igbp = mOutputIgbp;
    uint64_t bqId = mOutputBqId;
    uint32_t generation = mOutputGeneration;
    mOutputBufferQueueMutex.unlock();

    if (igbp) {
        holdBufferQueueBlocks(*flushedWork, igbp, bqId, generation);
    }

    return status;
}

c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) {
    Return<Status> transStatus = base()->drain(
            mode == C2Component::DRAIN_COMPONENT_WITH_EOS);
    if (!transStatus.isOk()) {
        ALOGE("drain -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("drain -- call failed. "
                "Error code = %d", static_cast<int>(status));
    }
    return status;
}

c2_status_t Codec2Client::Component::start() {
    Return<Status> transStatus = base()->start();
    if (!transStatus.isOk()) {
        ALOGE("start -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("start -- call failed. "
                "Error code = %d", static_cast<int>(status));
    }
    return status;
}

c2_status_t Codec2Client::Component::stop() {
    Return<Status> transStatus = base()->stop();
    if (!transStatus.isOk()) {
        ALOGE("stop -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("stop -- call failed. "
                "Error code = %d", static_cast<int>(status));
    }
    mInputBuffersMutex.lock();
    mInputBuffers.clear();
    mInputBufferCount.clear();
    mInputBuffersMutex.unlock();
    return status;
}

c2_status_t Codec2Client::Component::reset() {
    Return<Status> transStatus = base()->reset();
    if (!transStatus.isOk()) {
        ALOGE("reset -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("reset -- call failed. "
                "Error code = %d", static_cast<int>(status));
    }
    mInputBuffersMutex.lock();
    mInputBuffers.clear();
    mInputBufferCount.clear();
    mInputBuffersMutex.unlock();
    return status;
}

c2_status_t Codec2Client::Component::release() {
    Return<Status> transStatus = base()->release();
    if (!transStatus.isOk()) {
        ALOGE("release -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("release -- call failed. "
                "Error code = %d", static_cast<int>(status));
    }
    mInputBuffersMutex.lock();
    mInputBuffers.clear();
    mInputBufferCount.clear();
    mInputBuffersMutex.unlock();
    return status;
}

c2_status_t Codec2Client::Component::setOutputSurface(
        C2BlockPool::local_id_t blockPoolId,
        const sp<IGraphicBufferProducer>& surface,
        uint32_t generation) {
    sp<HGraphicBufferProducer> igbp =
            surface->getHalInterface<HGraphicBufferProducer>();
    if (!igbp) {
        igbp = new TWGraphicBufferProducer<HGraphicBufferProducer>(surface);
    }

    Return<Status> transStatus = base()->setOutputSurface(
            static_cast<uint64_t>(blockPoolId), igbp);
    if (!transStatus.isOk()) {
        ALOGE("setOutputSurface -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("setOutputSurface -- call failed. "
                "Error code = %d", static_cast<int>(status));
    } else {
        std::lock_guard<std::mutex> lock(mOutputBufferQueueMutex);
        if (mOutputIgbp != surface) {
            mOutputIgbp = surface;
            if (!surface) {
                mOutputBqId = 0;
            } else if (surface->getUniqueId(&mOutputBqId) != OK) {
                ALOGE("setOutputSurface -- cannot obtain bufferqueue id.");
            }
        }
        mOutputGeneration = generation;
    }
    return status;
}

status_t Codec2Client::Component::queueToOutputSurface(
        const C2ConstGraphicBlock& block,
        const QueueBufferInput& input,
        QueueBufferOutput* output) {
    uint32_t generation;
    uint64_t bqId;
    int32_t bqSlot;
    if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
            bqId == 0) {
        // Block not from bufferqueue -- it must be attached before queuing.

        mOutputBufferQueueMutex.lock();
        sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
        uint32_t outputGeneration = mOutputGeneration;
        mOutputBufferQueueMutex.unlock();

        status_t status = !attachToBufferQueue(block,
                                               outputIgbp,
                                               outputGeneration,
                                               &bqSlot);
        if (status != OK) {
            ALOGW("queueToOutputSurface -- attaching failed.");
            return INVALID_OPERATION;
        }

        status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
                                         input, output);
        if (status != OK) {
            ALOGE("queueToOutputSurface -- queueBuffer() failed "
                    "on non-bufferqueue-based block. "
                    "Error code = %d.",
                    static_cast<int>(status));
            return status;
        }
        return OK;
    }

    mOutputBufferQueueMutex.lock();
    sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
    uint64_t outputBqId = mOutputBqId;
    uint32_t outputGeneration = mOutputGeneration;
    mOutputBufferQueueMutex.unlock();

    if (!outputIgbp) {
        ALOGV("queueToOutputSurface -- output surface is null.");
        return NO_INIT;
    }

    if (bqId != outputBqId) {
        ALOGV("queueToOutputSurface -- bufferqueue ids mismatch.");
        return DEAD_OBJECT;
    }

    if (generation != outputGeneration) {
        ALOGV("queueToOutputSurface -- generation numbers mismatch.");
        return DEAD_OBJECT;
    }

    status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
                                              input, output);
    if (status != OK) {
        ALOGD("queueToOutputSurface -- queueBuffer() failed "
                "on bufferqueue-based block. "
                "Error code = %d.",
                static_cast<int>(status));
        return status;
    }
    if (!yieldBufferQueueBlock(block)) {
        ALOGD("queueToOutputSurface -- cannot yield bufferqueue-based block "
                "to the bufferqueue.");
        return UNKNOWN_ERROR;
    }
    return OK;
}

c2_status_t Codec2Client::Component::connectToOmxInputSurface(
        const sp<HGraphicBufferProducer>& producer,
        const sp<HGraphicBufferSource>& source) {
    Return<Status> transStatus = base()->connectToOmxInputSurface(
            producer, source);
    if (!transStatus.isOk()) {
        ALOGE("connectToOmxInputSurface -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("connectToOmxInputSurface -- call failed. "
                "Error code = %d", static_cast<int>(status));
    }
    return status;
}

c2_status_t Codec2Client::Component::disconnectFromInputSurface() {
    Return<Status> transStatus = base()->disconnectFromInputSurface();
    if (!transStatus.isOk()) {
        ALOGE("disconnectToInputSurface -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    c2_status_t status =
            static_cast<c2_status_t>(static_cast<Status>(transStatus));
    if (status != C2_OK) {
        ALOGE("disconnectFromInputSurface -- call failed. "
                "Error code = %d", static_cast<int>(status));
    }
    return status;
}

c2_status_t Codec2Client::Component::setDeathListener(
        const std::shared_ptr<Component>& component,
        const std::shared_ptr<Listener>& listener) {

    struct HidlDeathRecipient : public hardware::hidl_death_recipient {
        std::weak_ptr<Component> component;
        std::weak_ptr<Listener> base;

        virtual void serviceDied(
                uint64_t /* cookie */,
                const wp<::android::hidl::base::V1_0::IBase>& /* who */
                ) override {
            if (std::shared_ptr<Codec2Client::Listener> listener = base.lock()) {
                listener->onDeath(component);
            } else {
                ALOGW("onDeath -- listener died.");
            }
        }
    };

    sp<HidlDeathRecipient> deathRecipient = new HidlDeathRecipient();
    deathRecipient->base = listener;
    deathRecipient->component = component;

    component->mDeathRecipient = deathRecipient;
    Return<bool> transResult = component->base()->linkToDeath(
            component->mDeathRecipient, 0);
    if (!transResult.isOk()) {
        ALOGE("setDeathListener -- failed transaction: linkToDeath.");
        return C2_TRANSACTION_FAILED;
    }
    if (!static_cast<bool>(transResult)) {
        ALOGE("setDeathListener -- linkToDeath call failed.");
        return C2_CORRUPTED;
    }
    return C2_OK;
}

// Codec2Client::InputSurface

Codec2Client::InputSurface::Base* Codec2Client::InputSurface::base() const {
    return static_cast<Base*>(mBase.get());
}

Codec2Client::InputSurface::InputSurface(const sp<IInputSurface>& base) :
    mBase(base),
    mGraphicBufferProducer(new
            ::android::hardware::graphics::bufferqueue::V1_0::utils::
            H2BGraphicBufferProducer(base)) {
}

c2_status_t Codec2Client::InputSurface::connectToComponent(
        const std::shared_ptr<Codec2Client::Component>& component,
        std::shared_ptr<Connection>* connection) {
    c2_status_t status;
    Return<void> transStatus = base()->connectToComponent(
        component->base(),
        [&status, connection](
                Status s,
                const sp<IInputSurfaceConnection>& c) {
            status = static_cast<c2_status_t>(s);
            if (status != C2_OK) {
                ALOGE("connectToComponent -- call failed. "
                        "Error code = %d", static_cast<int>(status));
                return;
            }
            *connection = std::make_shared<Connection>(c);
        });
    if (!transStatus.isOk()) {
        ALOGE("connect -- transaction failed.");
        return C2_TRANSACTION_FAILED;
    }
    return status;
}

std::shared_ptr<Codec2Client::Configurable>
        Codec2Client::InputSurface::getConfigurable() const {
    Return<sp<IConfigurable>> transResult = base()->getConfigurable();
    if (!transResult.isOk()) {
        ALOGW("getConfigurable -- transaction failed.");
        return nullptr;
    }
    if (!static_cast<sp<IConfigurable>>(transResult)) {
        ALOGW("getConfigurable -- null pointer.");
        return nullptr;
    }
    return std::make_shared<Configurable>(transResult);
}

const sp<IGraphicBufferProducer>&
        Codec2Client::InputSurface::getGraphicBufferProducer() const {
    return mGraphicBufferProducer;
}

const sp<IInputSurface>& Codec2Client::InputSurface::getHalInterface() const {
    return mBase;
}

// Codec2Client::InputSurfaceConnection

Codec2Client::InputSurfaceConnection::Base*
        Codec2Client::InputSurfaceConnection::base() const {
    return static_cast<Base*>(mBase.get());
}

Codec2Client::InputSurfaceConnection::InputSurfaceConnection(
        const sp<Codec2Client::InputSurfaceConnection::Base>& base) :
    mBase(base) {
}

c2_status_t Codec2Client::InputSurfaceConnection::disconnect() {
    Return<Status> transResult = base()->disconnect();
    return static_cast<c2_status_t>(static_cast<Status>(transResult));
}

}  // namespace android