/* * 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 #include #include #include #include #include #include #include #include #include #include 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) : mComponent(component), mListener(component->mListener) { } virtual void onError_nb( std::weak_ptr /* c2component */, uint32_t errorCode) override { sp listener = mListener.promote(); if (listener) { Return 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 */, std::vector> c2settingResult ) override { sp listener = mListener.promote(); if (listener) { hidl_vec settingResults(c2settingResult.size()); size_t ix = 0; for (const std::shared_ptr &c2result : c2settingResult) { if (c2result) { if (!objcpy(&settingResults[ix++], *c2result)) { break; } } } settingResults.resize(ix); Return transStatus = listener->onTripped(settingResults); if (!transStatus.isOk()) { LOG(ERROR) << "Component::Listener::onTripped_nb -- " << "transaction failed."; } } } virtual void onWorkDone_nb( std::weak_ptr /* c2component */, std::list> c2workItems) override { for (const std::unique_ptr& 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 listener = mListener.promote(); if (listener) { WorkBundle workBundle; sp 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 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 mComponent; wp mListener; }; // Component::Sink struct Component::Sink : public IInputSink { std::shared_ptr mComponent; sp mConfigurable; virtual Return queue(const WorkBundle& workBundle) override { return mComponent->queue(workBundle); } virtual Return> getConfigurable() override { return mConfigurable; } Sink(const std::shared_ptr& component); virtual ~Sink() override; // Process-wide map: Component::Sink -> C2Component. static std::mutex sSink2ComponentMutex; static std::map> sSink2Component; static std::shared_ptr findLocalComponent( const sp& sink); }; std::mutex Component::Sink::sSink2ComponentMutex{}; std::map> Component::Sink::sSink2Component{}; Component::Sink::Sink(const std::shared_ptr& component) : mComponent{component}, mConfigurable{[&component]() -> sp { Return> ret1 = component->getInterface(); if (!ret1.isOk()) { LOG(ERROR) << "Sink::Sink -- component's transaction failed."; return nullptr; } Return> ret2 = static_cast>(ret1)-> getConfigurable(); if (!ret2.isOk()) { LOG(ERROR) << "Sink::Sink -- interface's transaction failed."; return nullptr; } return static_cast>(ret2); }()} { std::lock_guard lock(sSink2ComponentMutex); sSink2Component.emplace(this, component->mComponent); } Component::Sink::~Sink() { std::lock_guard lock(sSink2ComponentMutex); sSink2Component.erase(this); } std::shared_ptr Component::Sink::findLocalComponent( const sp& sink) { std::lock_guard 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& component, const sp& listener, const sp& 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 Component::queue(const WorkBundle& workBundle) { std::list> c2works; if (!objcpy(&c2works, workBundle)) { return Status::CORRUPTED; } // Register input buffers. for (const std::unique_ptr& work : c2works) { if (work) { InputBufferManager:: registerFrameData(mListener, work->input); } } return static_cast(mComponent->queue_nb(&c2works)); } Return Component::flush(flush_cb _hidl_cb) { std::list> c2flushedWorks; c2_status_t c2res = mComponent->flush_sm( C2Component::FLUSH_COMPONENT, &c2flushedWorks); // Unregister input buffers. for (const std::unique_ptr& 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(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 Component::drain(bool withEos) { return static_cast(mComponent->drain_nb(withEos ? C2Component::DRAIN_COMPONENT_WITH_EOS : C2Component::DRAIN_COMPONENT_NO_EOS)); } Return Component::setOutputSurface( uint64_t blockPoolId, const sp& surface) { std::shared_ptr pool; GetCodec2BlockPool(blockPoolId, mComponent, &pool); if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) { std::shared_ptr bqPool = std::static_pointer_cast(pool); C2BufferQueueBlockPool::OnRenderCallback cb = [this](uint64_t producer, int32_t slot, int64_t nsecs) { // TODO: batch this hidl_vec 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 Component::connectToInputSurface( const sp& inputSurface, connectToInputSurface_cb _hidl_cb) { Status status; sp connection; auto transStatus = inputSurface->connect( asInputSink(), [&status, &connection]( Status s, const sp& c) { status = s; connection = c; } ); _hidl_cb(status, connection); return Void(); } Return Component::connectToOmxInputSurface( const sp& 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 Component::disconnectFromInputSurface() { // TODO implement return Status::OK; } namespace /* unnamed */ { struct BlockPoolIntf : public ConfigurableC2Intf { BlockPoolIntf(const std::shared_ptr& pool) : ConfigurableC2Intf{ "C2BlockPool:" + (pool ? std::to_string(pool->getLocalId()) : "null"), 0}, mPool{pool} { } virtual c2_status_t config( const std::vector& params, c2_blocking_t mayBlock, std::vector>* const failures ) override { (void)params; (void)mayBlock; (void)failures; return C2_OK; } virtual c2_status_t query( const std::vector& indices, c2_blocking_t mayBlock, std::vector>* const params ) const override { (void)indices; (void)mayBlock; (void)params; return C2_OK; } virtual c2_status_t querySupportedParams( std::vector>* const params ) const override { (void)params; return C2_OK; } virtual c2_status_t querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const override { (void)fields; (void)mayBlock; return C2_OK; } protected: std::shared_ptr mPool; }; } // unnamed namespace Return Component::createBlockPool( uint32_t allocatorId, createBlockPool_cb _hidl_cb) { std::shared_ptr blockPool; c2_status_t status = CreateCodec2BlockPool( static_cast(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), blockPool ? blockPool->getLocalId() : 0, new CachedConfigurable( std::make_unique(blockPool))); return Void(); } Return Component::destroyBlockPool(uint64_t blockPoolId) { std::lock_guard lock(mBlockPoolsMutex); return mBlockPools.erase(blockPoolId) == 1 ? Status::OK : Status::CORRUPTED; } Return Component::start() { return static_cast(mComponent->start()); } Return Component::stop() { InputBufferManager::unregisterFrameData(mListener); return static_cast(mComponent->stop()); } Return Component::reset() { Status status = static_cast(mComponent->reset()); { std::lock_guard lock(mBlockPoolsMutex); mBlockPools.clear(); } InputBufferManager::unregisterFrameData(mListener); return status; } Return Component::release() { Status status = static_cast(mComponent->release()); { std::lock_guard lock(mBlockPoolsMutex); mBlockPools.clear(); } InputBufferManager::unregisterFrameData(mListener); return status; } Return> Component::getInterface() { return sp(mInterface); } Return> Component::asInputSink() { std::lock_guard lock(mSinkMutex); if (!mSink) { mSink = new Sink(shared_from_this()); } return {mSink}; } std::shared_ptr Component::findLocalComponent( const sp& sink) { return Component::Sink::findLocalComponent(sink); } void Component::initListener(const sp& self) { std::shared_ptr c2listener = std::make_shared(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