| /* |
| * 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."); |
| } |
| |
| // Finish buffer transfers: nothing else to do |
| } |
| } |
| |
| 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) { |
| // TODO: Connect with bufferpool API for buffer transfers |
| ALOGV("flush -- converting output"); |
| res = objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender); |
| } |
| _hidl_cb(res, flushedWorkBundle); |
| 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 |