| /* |
| * Copyright 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 <android-base/logging.h> |
| |
| #include <codec2/hidl/1.0/Component.h> |
| #include <codec2/hidl/1.0/ComponentStore.h> |
| #include <codec2/hidl/1.0/InputBufferManager.h> |
| |
| #include <hidl/HidlBinderSupport.h> |
| #include <utils/Timers.h> |
| |
| #include <C2BqBufferPriv.h> |
| #include <C2Debug.h> |
| #include <C2PlatformSupport.h> |
| |
| #include <chrono> |
| #include <thread> |
| |
| namespace android { |
| namespace hardware { |
| namespace media { |
| namespace c2 { |
| namespace V1_0 { |
| namespace utils { |
| |
| using namespace ::android; |
| |
| // 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 { |
| sp<IComponentListener> listener = mListener.promote(); |
| if (listener) { |
| Return<void> transStatus = listener->onError(Status::OK, errorCode); |
| if (!transStatus.isOk()) { |
| LOG(ERROR) << "Component::Listener::onError_nb -- " |
| << "transaction failed."; |
| } |
| } |
| } |
| |
| virtual void onTripped_nb( |
| std::weak_ptr<C2Component> /* c2component */, |
| std::vector<std::shared_ptr<C2SettingResult>> c2settingResult |
| ) override { |
| 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)) { |
| break; |
| } |
| } |
| } |
| settingResults.resize(ix); |
| Return<void> transStatus = listener->onTripped(settingResults); |
| if (!transStatus.isOk()) { |
| LOG(ERROR) << "Component::Listener::onTripped_nb -- " |
| << "transaction failed."; |
| } |
| } |
| } |
| |
| virtual void onWorkDone_nb( |
| std::weak_ptr<C2Component> /* c2component */, |
| std::list<std::unique_ptr<C2Work>> c2workItems) override { |
| for (const std::unique_ptr<C2Work>& work : c2workItems) { |
| if (work) { |
| if (work->worklets.empty() |
| || !work->worklets.back() |
| || (work->worklets.back()->output.flags & |
| C2FrameData::FLAG_INCOMPLETE) == 0) { |
| InputBufferManager:: |
| unregisterFrameData(mListener, work->input); |
| } |
| } |
| } |
| |
| sp<IComponentListener> listener = mListener.promote(); |
| if (listener) { |
| WorkBundle workBundle; |
| |
| sp<Component> strongComponent = mComponent.promote(); |
| beginTransferBufferQueueBlocks(c2workItems, true); |
| if (!objcpy(&workBundle, c2workItems, strongComponent ? |
| &strongComponent->mBufferPoolSender : nullptr)) { |
| LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " |
| << "received corrupted work items."; |
| endTransferBufferQueueBlocks(c2workItems, false, true); |
| return; |
| } |
| Return<void> transStatus = listener->onWorkDone(workBundle); |
| if (!transStatus.isOk()) { |
| LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " |
| << "transaction failed."; |
| endTransferBufferQueueBlocks(c2workItems, false, true); |
| return; |
| } |
| endTransferBufferQueueBlocks(c2workItems, true, true); |
| } |
| } |
| |
| protected: |
| wp<Component> mComponent; |
| wp<IComponentListener> mListener; |
| }; |
| |
| // Component::Sink |
| struct Component::Sink : public IInputSink { |
| std::shared_ptr<Component> mComponent; |
| sp<IConfigurable> mConfigurable; |
| |
| virtual Return<Status> queue(const WorkBundle& workBundle) override { |
| return mComponent->queue(workBundle); |
| } |
| |
| virtual Return<sp<IConfigurable>> getConfigurable() override { |
| return mConfigurable; |
| } |
| |
| Sink(const std::shared_ptr<Component>& component); |
| virtual ~Sink() override; |
| |
| // Process-wide map: Component::Sink -> C2Component. |
| static std::mutex sSink2ComponentMutex; |
| static std::map<IInputSink*, std::weak_ptr<C2Component>> sSink2Component; |
| |
| static std::shared_ptr<C2Component> findLocalComponent( |
| const sp<IInputSink>& sink); |
| }; |
| |
| std::mutex |
| Component::Sink::sSink2ComponentMutex{}; |
| std::map<IInputSink*, std::weak_ptr<C2Component>> |
| Component::Sink::sSink2Component{}; |
| |
| Component::Sink::Sink(const std::shared_ptr<Component>& component) |
| : mComponent{component}, |
| mConfigurable{[&component]() -> sp<IConfigurable> { |
| Return<sp<IComponentInterface>> ret1 = component->getInterface(); |
| if (!ret1.isOk()) { |
| LOG(ERROR) << "Sink::Sink -- component's transaction failed."; |
| return nullptr; |
| } |
| Return<sp<IConfigurable>> ret2 = |
| static_cast<sp<IComponentInterface>>(ret1)-> |
| getConfigurable(); |
| if (!ret2.isOk()) { |
| LOG(ERROR) << "Sink::Sink -- interface's transaction failed."; |
| return nullptr; |
| } |
| return static_cast<sp<IConfigurable>>(ret2); |
| }()} { |
| std::lock_guard<std::mutex> lock(sSink2ComponentMutex); |
| sSink2Component.emplace(this, component->mComponent); |
| } |
| |
| Component::Sink::~Sink() { |
| std::lock_guard<std::mutex> lock(sSink2ComponentMutex); |
| sSink2Component.erase(this); |
| } |
| |
| std::shared_ptr<C2Component> Component::Sink::findLocalComponent( |
| const sp<IInputSink>& sink) { |
| std::lock_guard<std::mutex> lock(sSink2ComponentMutex); |
| auto i = sSink2Component.find(sink.get()); |
| if (i == sSink2Component.end()) { |
| return nullptr; |
| } |
| return i->second.lock(); |
| } |
| |
| // Component |
| Component::Component( |
| const std::shared_ptr<C2Component>& component, |
| const sp<IComponentListener>& listener, |
| const sp<ComponentStore>& store, |
| const sp<::android::hardware::media::bufferpool::V2_0:: |
| IClientManager>& clientPoolManager) |
| : mComponent{component}, |
| mInterface{new ComponentInterface(component->intf(), |
| store->getParameterCache())}, |
| mListener{listener}, |
| mStore{store}, |
| mBufferPoolSender{clientPoolManager} { |
| // Retrieve supported parameters from store |
| // TODO: We could cache this per component/interface type |
| mInit = mInterface->status(); |
| } |
| |
| c2_status_t Component::status() const { |
| return mInit; |
| } |
| |
| // Methods from ::android::hardware::media::c2::V1_0::IComponent |
| Return<Status> Component::queue(const WorkBundle& workBundle) { |
| std::list<std::unique_ptr<C2Work>> c2works; |
| |
| if (!objcpy(&c2works, workBundle)) { |
| return Status::CORRUPTED; |
| } |
| |
| // Register input buffers. |
| for (const std::unique_ptr<C2Work>& work : c2works) { |
| if (work) { |
| InputBufferManager:: |
| registerFrameData(mListener, work->input); |
| } |
| } |
| |
| return static_cast<Status>(mComponent->queue_nb(&c2works)); |
| } |
| |
| Return<void> Component::flush(flush_cb _hidl_cb) { |
| std::list<std::unique_ptr<C2Work>> c2flushedWorks; |
| c2_status_t c2res = mComponent->flush_sm( |
| C2Component::FLUSH_COMPONENT, |
| &c2flushedWorks); |
| |
| // Unregister input buffers. |
| for (const std::unique_ptr<C2Work>& work : c2flushedWorks) { |
| if (work) { |
| if (work->worklets.empty() |
| || !work->worklets.back() |
| || (work->worklets.back()->output.flags & |
| C2FrameData::FLAG_INCOMPLETE) == 0) { |
| InputBufferManager:: |
| unregisterFrameData(mListener, work->input); |
| } |
| } |
| } |
| |
| WorkBundle flushedWorkBundle; |
| Status res = static_cast<Status>(c2res); |
| beginTransferBufferQueueBlocks(c2flushedWorks, true); |
| if (c2res == C2_OK) { |
| if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) { |
| res = Status::CORRUPTED; |
| } |
| } |
| _hidl_cb(res, flushedWorkBundle); |
| endTransferBufferQueueBlocks(c2flushedWorks, true, true); |
| return Void(); |
| } |
| |
| Return<Status> Component::drain(bool withEos) { |
| 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<HGraphicBufferProducer2>& 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 }; |
| (void)mListener->onFramesRendered(rendered).isOk(); |
| }; |
| if (bqPool) { |
| bqPool->setRenderCallback(cb); |
| bqPool->configureProducer(surface); |
| } |
| } |
| return Status::OK; |
| } |
| |
| Return<void> Component::connectToInputSurface( |
| const sp<IInputSurface>& inputSurface, |
| connectToInputSurface_cb _hidl_cb) { |
| Status status; |
| sp<IInputSurfaceConnection> connection; |
| auto transStatus = inputSurface->connect( |
| asInputSink(), |
| [&status, &connection]( |
| Status s, const sp<IInputSurfaceConnection>& c) { |
| status = s; |
| connection = c; |
| } |
| ); |
| _hidl_cb(status, connection); |
| return Void(); |
| } |
| |
| Return<void> Component::connectToOmxInputSurface( |
| const sp<HGraphicBufferProducer1>& producer, |
| const sp<::android::hardware::media::omx::V1_0:: |
| IGraphicBufferSource>& source, |
| connectToOmxInputSurface_cb _hidl_cb) { |
| (void)producer; |
| (void)source; |
| (void)_hidl_cb; |
| return Void(); |
| } |
| |
| Return<Status> Component::disconnectFromInputSurface() { |
| // TODO implement |
| return Status::OK; |
| } |
| |
| namespace /* unnamed */ { |
| |
| struct BlockPoolIntf : public ConfigurableC2Intf { |
| BlockPoolIntf(const std::shared_ptr<C2BlockPool>& pool) |
| : ConfigurableC2Intf{ |
| "C2BlockPool:" + |
| (pool ? std::to_string(pool->getLocalId()) : "null"), |
| 0}, |
| 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() { |
| return static_cast<Status>(mComponent->start()); |
| } |
| |
| Return<Status> Component::stop() { |
| InputBufferManager::unregisterFrameData(mListener); |
| return static_cast<Status>(mComponent->stop()); |
| } |
| |
| Return<Status> Component::reset() { |
| Status status = static_cast<Status>(mComponent->reset()); |
| { |
| std::lock_guard<std::mutex> lock(mBlockPoolsMutex); |
| mBlockPools.clear(); |
| } |
| InputBufferManager::unregisterFrameData(mListener); |
| return status; |
| } |
| |
| Return<Status> Component::release() { |
| Status status = static_cast<Status>(mComponent->release()); |
| { |
| std::lock_guard<std::mutex> lock(mBlockPoolsMutex); |
| mBlockPools.clear(); |
| } |
| InputBufferManager::unregisterFrameData(mListener); |
| return status; |
| } |
| |
| Return<sp<IComponentInterface>> Component::getInterface() { |
| return sp<IComponentInterface>(mInterface); |
| } |
| |
| Return<sp<IInputSink>> Component::asInputSink() { |
| std::lock_guard<std::mutex> lock(mSinkMutex); |
| if (!mSink) { |
| mSink = new Sink(shared_from_this()); |
| } |
| return {mSink}; |
| } |
| |
| std::shared_ptr<C2Component> Component::findLocalComponent( |
| const sp<IInputSink>& sink) { |
| return Component::Sink::findLocalComponent(sink); |
| } |
| |
| 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() { |
| InputBufferManager::unregisterFrameData(mListener); |
| mStore->reportComponentDeath(this); |
| } |
| |
| } // namespace utils |
| } // namespace V1_0 |
| } // namespace c2 |
| } // namespace media |
| } // namespace hardware |
| } // namespace android |
| |