/* * 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 "Codec2-Component" #include <log/log.h> #include <C2PlatformSupport.h> #include <codec2/hidl/1.0/Component.h> #include <codec2/hidl/1.0/ComponentStore.h> #include <codec2/hidl/1.0/types.h> #include <hidl/HidlBinderSupport.h> #include <C2BqBufferPriv.h> #include <C2PlatformSupport.h> namespace hardware { namespace google { namespace media { namespace c2 { namespace V1_0 { namespace utils { using namespace ::android; namespace /* unnamed */ { // Implementation of ConfigurableC2Intf based on C2ComponentInterface struct CompIntf : public ConfigurableC2Intf { CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) : ConfigurableC2Intf(intf->getName()), mIntf(intf) { } virtual c2_status_t config( const std::vector<C2Param*>& params, c2_blocking_t mayBlock, std::vector<std::unique_ptr<C2SettingResult>>* const failures ) override { ALOGV("config"); return mIntf->config_vb(params, mayBlock, failures); } virtual c2_status_t query( const std::vector<C2Param::Index>& indices, c2_blocking_t mayBlock, std::vector<std::unique_ptr<C2Param>>* const params ) const override { ALOGV("query"); return mIntf->query_vb({}, indices, mayBlock, params); } virtual c2_status_t querySupportedParams( std::vector<std::shared_ptr<C2ParamDescriptor>>* const params ) const override { ALOGV("querySupportedParams"); return mIntf->querySupportedParams_nb(params); } virtual c2_status_t querySupportedValues( std::vector<C2FieldSupportedValuesQuery>& fields, c2_blocking_t mayBlock) const override { ALOGV("querySupportedValues"); return mIntf->querySupportedValues_vb(fields, mayBlock); } protected: std::shared_ptr<C2ComponentInterface> mIntf; }; } // unnamed namespace // ComponentInterface ComponentInterface::ComponentInterface( const std::shared_ptr<C2ComponentInterface>& intf, const sp<ComponentStore>& store) : Configurable(new CachedConfigurable(std::make_unique<CompIntf>(intf))), mInterface(intf) { mInit = init(store.get()); } c2_status_t ComponentInterface::status() const { return mInit; } // ComponentListener wrapper struct Component::Listener : public C2Component::Listener { Listener(const sp<Component>& component) : mComponent(component), mListener(component->mListener) { } virtual void onError_nb( std::weak_ptr<C2Component> /* c2component */, uint32_t errorCode) override { ALOGV("onError"); sp<IComponentListener> listener = mListener.promote(); if (listener) { Return<void> transStatus = listener->onError(Status::OK, errorCode); if (!transStatus.isOk()) { ALOGE("onError -- transaction failed."); } } } virtual void onTripped_nb( std::weak_ptr<C2Component> /* c2component */, std::vector<std::shared_ptr<C2SettingResult>> c2settingResult ) override { ALOGV("onTripped"); sp<IComponentListener> listener = mListener.promote(); if (listener) { hidl_vec<SettingResult> settingResults(c2settingResult.size()); size_t ix = 0; for (const std::shared_ptr<C2SettingResult> &c2result : c2settingResult) { if (c2result) { if (objcpy(&settingResults[ix++], *c2result) != Status::OK) { break; } } } settingResults.resize(ix); Return<void> transStatus = listener->onTripped(settingResults); if (!transStatus.isOk()) { ALOGE("onTripped -- transaction failed."); } } } virtual void onWorkDone_nb( std::weak_ptr<C2Component> /* c2component */, std::list<std::unique_ptr<C2Work>> c2workItems) override { ALOGV("onWorkDone"); sp<IComponentListener> listener = mListener.promote(); if (listener) { WorkBundle workBundle; sp<Component> strongComponent = mComponent.promote(); if (objcpy(&workBundle, c2workItems, strongComponent ? &strongComponent->mBufferPoolSender : nullptr) != Status::OK) { ALOGE("onWorkDone() received corrupted work items."); return; } Return<void> transStatus = listener->onWorkDone(workBundle); if (!transStatus.isOk()) { ALOGE("onWorkDone -- transaction failed."); return; } yieldBufferQueueBlocks(c2workItems, true); } } protected: wp<Component> mComponent; wp<IComponentListener> mListener; }; // Component Component::Component( const std::shared_ptr<C2Component>& component, const sp<IComponentListener>& listener, const sp<ComponentStore>& store, const sp<::android::hardware::media::bufferpool::V1_0:: IClientManager>& clientPoolManager) : Configurable(new CachedConfigurable( std::make_unique<CompIntf>(component->intf()))), mComponent(component), mInterface(component->intf()), mListener(listener), mStore(store), mBufferPoolSender(clientPoolManager) { // Retrieve supported parameters from store // TODO: We could cache this per component/interface type mInit = init(store.get()); } c2_status_t Component::status() const { return mInit; } // Methods from ::android::hardware::media::c2::V1_0::IComponent Return<Status> Component::queue(const WorkBundle& workBundle) { ALOGV("queue -- converting input"); std::list<std::unique_ptr<C2Work>> c2works; // TODO: Connect with bufferpool API for buffer transfers if (objcpy(&c2works, workBundle) != C2_OK) { ALOGV("queue -- corrupted"); return Status::CORRUPTED; } ALOGV("queue -- calling"); return static_cast<Status>(mComponent->queue_nb(&c2works)); } Return<void> Component::flush(flush_cb _hidl_cb) { std::list<std::unique_ptr<C2Work>> c2flushedWorks; ALOGV("flush -- calling"); c2_status_t c2res = mComponent->flush_sm( C2Component::FLUSH_COMPONENT, &c2flushedWorks); WorkBundle flushedWorkBundle; Status res = static_cast<Status>(c2res); if (c2res == C2_OK) { ALOGV("flush -- converting output"); res = objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender); } _hidl_cb(res, flushedWorkBundle); yieldBufferQueueBlocks(c2flushedWorks, true); return Void(); } Return<Status> Component::drain(bool withEos) { ALOGV("drain"); return static_cast<Status>(mComponent->drain_nb(withEos ? C2Component::DRAIN_COMPONENT_WITH_EOS : C2Component::DRAIN_COMPONENT_NO_EOS)); } Return<Status> Component::setOutputSurface( uint64_t blockPoolId, const sp<HGraphicBufferProducer>& surface) { std::shared_ptr<C2BlockPool> pool; GetCodec2BlockPool(blockPoolId, mComponent, &pool); if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) { std::shared_ptr<C2BufferQueueBlockPool> bqPool = std::static_pointer_cast<C2BufferQueueBlockPool>(pool); C2BufferQueueBlockPool::OnRenderCallback cb = [this](uint64_t producer, int32_t slot, int64_t nsecs) { // TODO: batch this hidl_vec<IComponentListener::RenderedFrame> rendered; rendered.resize(1); rendered[0] = { producer, slot, nsecs }; mListener->onFramesRendered(rendered); }; if (bqPool) { bqPool->setRenderCallback(cb); bqPool->configureProducer(surface); } } return Status::OK; } Return<Status> Component::connectToOmxInputSurface( const sp<HGraphicBufferProducer>& producer, const sp<::android::hardware::media::omx::V1_0:: IGraphicBufferSource>& source) { // TODO implement (void)producer; (void)source; return Status::OMITTED; } Return<Status> Component::disconnectFromInputSurface() { // TODO implement return Status::OK; } namespace /* unnamed */ { struct BlockPoolIntf : public ConfigurableC2Intf { BlockPoolIntf(const std::shared_ptr<C2BlockPool>& pool) : ConfigurableC2Intf("C2BlockPool:" + std::to_string(pool->getLocalId())), mPool(pool) { } virtual c2_status_t config( const std::vector<C2Param*>& params, c2_blocking_t mayBlock, std::vector<std::unique_ptr<C2SettingResult>>* const failures ) override { (void)params; (void)mayBlock; (void)failures; return C2_OK; } virtual c2_status_t query( const std::vector<C2Param::Index>& indices, c2_blocking_t mayBlock, std::vector<std::unique_ptr<C2Param>>* const params ) const override { (void)indices; (void)mayBlock; (void)params; return C2_OK; } virtual c2_status_t querySupportedParams( std::vector<std::shared_ptr<C2ParamDescriptor>>* const params ) const override { (void)params; return C2_OK; } virtual c2_status_t querySupportedValues( std::vector<C2FieldSupportedValuesQuery>& fields, c2_blocking_t mayBlock) const override { (void)fields; (void)mayBlock; return C2_OK; } protected: std::shared_ptr<C2BlockPool> mPool; }; } // unnamed namespace Return<void> Component::createBlockPool( uint32_t allocatorId, createBlockPool_cb _hidl_cb) { std::shared_ptr<C2BlockPool> blockPool; c2_status_t status = CreateCodec2BlockPool( static_cast<C2PlatformAllocatorStore::id_t>(allocatorId), mComponent, &blockPool); if (status != C2_OK) { blockPool = nullptr; } if (blockPool) { mBlockPoolsMutex.lock(); mBlockPools.emplace(blockPool->getLocalId(), blockPool); mBlockPoolsMutex.unlock(); } else if (status == C2_OK) { status = C2_CORRUPTED; } _hidl_cb(static_cast<Status>(status), blockPool ? blockPool->getLocalId() : 0, new CachedConfigurable( std::make_unique<BlockPoolIntf>(blockPool))); return Void(); } Return<Status> Component::destroyBlockPool(uint64_t blockPoolId) { std::lock_guard<std::mutex> lock(mBlockPoolsMutex); return mBlockPools.erase(blockPoolId) == 1 ? Status::OK : Status::CORRUPTED; } Return<Status> Component::start() { ALOGV("start"); return static_cast<Status>(mComponent->start()); } Return<Status> Component::stop() { ALOGV("stop"); return static_cast<Status>(mComponent->stop()); } Return<Status> Component::reset() { ALOGV("reset"); Status status = static_cast<Status>(mComponent->reset()); { std::lock_guard<std::mutex> lock(mBlockPoolsMutex); mBlockPools.clear(); } return status; } Return<Status> Component::release() { ALOGV("release"); Status status = static_cast<Status>(mComponent->release()); { std::lock_guard<std::mutex> lock(mBlockPoolsMutex); mBlockPools.clear(); } return status; } void Component::setLocalId(const Component::LocalId& localId) { mLocalId = localId; } void Component::initListener(const sp<Component>& self) { std::shared_ptr<C2Component::Listener> c2listener = std::make_shared<Listener>(self); c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK); if (res != C2_OK) { mInit = res; } } Component::~Component() { mStore->reportComponentDeath(mLocalId); } Component::InterfaceKey::InterfaceKey(const sp<IComponent>& component) { isRemote = component->isRemote(); if (isRemote) { remote = ::android::hardware::toBinder(component); } else { local = component; } } } // namespace utils } // namespace V1_0 } // namespace c2 } // namespace media } // namespace google } // namespace hardware