/* * 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 "Codec2Client" #define ATRACE_TAG ATRACE_TAG_VIDEO #include #include #include #include #include #include #include // for C2StreamUsageTuning #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for GRALLOC_USAGE_* #include #include // for NATIVE_WINDOW_QUERY_* #include // for asString(status_t) #include #include #include #include #include #include #include #include #include #include namespace android { using ::android::hardware::hidl_vec; using ::android::hardware::hidl_string; using ::android::hardware::Return; using ::android::hardware::Void; using HGraphicBufferProducer1 = ::android::hardware::graphics::bufferqueue:: V1_0::IGraphicBufferProducer; using HGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue:: V2_0::IGraphicBufferProducer; using B2HGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue:: V2_0::utils::B2HGraphicBufferProducer; using H2BGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue:: V2_0::utils::H2BGraphicBufferProducer; using ::android::hardware::media::c2::V1_2::SurfaceSyncObj; using AidlGraphicBufferAllocator = ::aidl::android::hardware::media::c2:: implementation::GraphicBufferAllocator; namespace bufferpool2_aidl = ::aidl::android::hardware::media::bufferpool2; namespace bufferpool_hidl = ::android::hardware::media::bufferpool::V2_0; namespace c2_aidl = ::aidl::android::hardware::media::c2; namespace c2_hidl_base = ::android::hardware::media::c2; namespace c2_hidl = ::android::hardware::media::c2::V1_2; using c2_hidl::utils::operator<<; namespace /* unnamed */ { // c2_status_t value that corresponds to hwbinder transaction failure. constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED; // By default prepare buffer to be displayed on any of the common surfaces constexpr uint64_t kDefaultConsumerUsage = (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER); // Searches for a name in GetServiceNames() and returns the index found. If the // name is not found, the returned index will be equal to // GetServiceNames().size(). size_t getServiceIndex(char const* name) { std::vector const& names = Codec2Client::GetServiceNames(); size_t i = 0; for (; i < names.size(); ++i) { if (name == names[i]) { break; } } return i; } class Client2Store : public C2ComponentStore { std::shared_ptr mClient; public: Client2Store(std::shared_ptr const& client) : mClient(client) { } virtual ~Client2Store() = default; virtual c2_status_t config_sm( std::vector const ¶ms, std::vector>* const failures) { return mClient->config(params, C2_MAY_BLOCK, failures); }; virtual c2_status_t copyBuffer( std::shared_ptr, std::shared_ptr) { return C2_OMITTED; } virtual c2_status_t createComponent( C2String, std::shared_ptr* const component) { component->reset(); return C2_OMITTED; } virtual c2_status_t createInterface( C2String, std::shared_ptr* const interface) { interface->reset(); return C2_OMITTED; } virtual c2_status_t query_sm( std::vector const& stackParams, std::vector const& heapParamIndices, std::vector>* const heapParams) const { return mClient->query(stackParams, heapParamIndices, C2_MAY_BLOCK, heapParams); } virtual c2_status_t querySupportedParams_nb( std::vector>* const params) const { return mClient->querySupportedParams(params); } virtual c2_status_t querySupportedValues_sm( std::vector& fields) const { return mClient->querySupportedValues(fields, C2_MAY_BLOCK); } virtual C2String getName() const { return mClient->getName(); } virtual std::shared_ptr getParamReflector() const { return mClient->getParamReflector(); } virtual std::vector> listComponents() { return std::vector>(); } }; c2_status_t GetC2Status(const ::ndk::ScopedAStatus &transStatus, const char *method) { if (!transStatus.isOk()) { if (transStatus.getExceptionCode() == EX_SERVICE_SPECIFIC) { c2_status_t status = static_cast(transStatus.getServiceSpecificError()); LOG(DEBUG) << method << " -- call failed: " << status << "."; return status; } else { LOG(ERROR) << method << " -- transaction failed."; return C2_TRANSACTION_FAILED; } } return C2_OK; } } // unnamed namespace // This class caches a Codec2Client object and its component traits. The client // will be created the first time it is needed, and it can be refreshed if the // service dies (by calling invalidate()). The first time listComponents() is // called from the client, the result will be cached. class Codec2Client::Cache { // Cached client std::shared_ptr mClient; mutable std::mutex mClientMutex; // Cached component traits std::vector mTraits; std::once_flag mTraitsInitializationFlag; // The index of the service. This is based on GetServiceNames(). size_t mIndex; // Called by s() exactly once to initialize the cache. The index must be a // valid index into the vector returned by GetServiceNames(). Calling // init(index) will associate the cache to the service with name // GetServiceNames()[index]. void init(size_t index) { mIndex = index; } public: Cache() = default; // Initializes mClient if needed, then returns mClient. // If the service is unavailable but listed in the manifest, this function // will block indefinitely. std::shared_ptr getClient() { std::scoped_lock lock{mClientMutex}; if (!mClient) { mClient = Codec2Client::_CreateFromIndex(mIndex); } CHECK(mClient) << "Failed to create Codec2Client to service \"" << GetServiceNames()[mIndex] << "\". (Index = " << mIndex << ")."; return mClient; } // Causes a subsequent call to getClient() to create a new client. This // function should be called after the service dies. // // Note: This function is called only by ForAllServices(). void invalidate() { std::scoped_lock lock{mClientMutex}; mClient = nullptr; } // Returns a list of traits for components supported by the service. This // list is cached. std::vector const& getTraits() { std::call_once(mTraitsInitializationFlag, [this]() { bool success{false}; // Spin until _listComponents() is successful. while (true) { std::shared_ptr client = getClient(); mTraits = client->_listComponents(&success); if (success) { break; } invalidate(); using namespace std::chrono_literals; static constexpr auto kServiceRetryPeriod = 5s; LOG(INFO) << "Failed to retrieve component traits from service " "\"" << GetServiceNames()[mIndex] << "\". " "Retrying..."; std::this_thread::sleep_for(kServiceRetryPeriod); } }); return mTraits; } // List() returns the list of all caches. static std::vector& List() { static std::vector sCaches{[]() { size_t numServices = GetServiceNames().size(); std::vector caches(numServices); for (size_t i = 0; i < numServices; ++i) { caches[i].init(i); } return caches; }()}; return sCaches; } }; // Codec2ConfigurableClient::HidlImpl struct Codec2ConfigurableClient::HidlImpl : public Codec2ConfigurableClient::ImplBase { typedef c2_hidl::IConfigurable Base; // base cannot be null. explicit HidlImpl(const sp& base); const C2String& getName() const override { return mName; } c2_status_t query( const std::vector& stackParams, const std::vector &heapParamIndices, c2_blocking_t mayBlock, std::vector>* const heapParams) const override; c2_status_t config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector>* const failures) override; c2_status_t querySupportedParams( std::vector>* const params ) const override; c2_status_t querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const override; private: sp mBase; const C2String mName; }; Codec2ConfigurableClient::HidlImpl::HidlImpl(const sp& base) : mBase{base}, mName{[base]() -> C2String { C2String outName; Return transStatus = base->getName( [&outName](const hidl_string& name) { outName = name.c_str(); }); return transStatus.isOk() ? outName : ""; }()} { } c2_status_t Codec2ConfigurableClient::HidlImpl::query( const std::vector &stackParams, const std::vector &heapParamIndices, c2_blocking_t mayBlock, std::vector>* const heapParams) const { hidl_vec indices( stackParams.size() + heapParamIndices.size()); size_t numIndices = 0; for (C2Param* const& stackParam : stackParams) { if (!stackParam) { LOG(WARNING) << "query -- null stack param encountered."; continue; } indices[numIndices++] = static_cast(stackParam->index()); } size_t numStackIndices = numIndices; for (const C2Param::Index& index : heapParamIndices) { indices[numIndices++] = static_cast(static_cast(index)); } indices.resize(numIndices); if (heapParams) { heapParams->reserve(heapParams->size() + numIndices); } c2_status_t status; Return transStatus = mBase->query( indices, mayBlock == C2_MAY_BLOCK, [&status, &numStackIndices, &stackParams, heapParams]( c2_hidl::Status s, const c2_hidl::Params& p) { status = static_cast(s); if (status != C2_OK && status != C2_BAD_INDEX) { LOG(DEBUG) << "query -- call failed: " << status << "."; return; } std::vector paramPointers; if (!c2_hidl::utils::parseParamsBlob(¶mPointers, p)) { LOG(ERROR) << "query -- error while parsing params."; status = C2_CORRUPTED; return; } size_t i = 0; for (auto it = paramPointers.begin(); it != paramPointers.end(); ) { C2Param* paramPointer = *it; if (numStackIndices > 0) { --numStackIndices; if (!paramPointer) { LOG(WARNING) << "query -- null stack param."; ++it; continue; } for (; i < stackParams.size() && !stackParams[i]; ) { ++i; } if (i >= stackParams.size()) { LOG(ERROR) << "query -- unexpected error."; status = C2_CORRUPTED; return; } if (stackParams[i]->index() != paramPointer->index()) { LOG(WARNING) << "query -- param skipped: " "index = " << stackParams[i]->index() << "."; stackParams[i++]->invalidate(); continue; } if (!stackParams[i++]->updateFrom(*paramPointer)) { LOG(WARNING) << "query -- param update failed: " "index = " << paramPointer->index() << "."; } } else { if (!paramPointer) { LOG(WARNING) << "query -- null heap param."; ++it; continue; } if (!heapParams) { LOG(WARNING) << "query -- " "unexpected extra stack param."; } else { heapParams->emplace_back( C2Param::Copy(*paramPointer)); } } ++it; } }); if (!transStatus.isOk()) { LOG(ERROR) << "query -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; } c2_status_t Codec2ConfigurableClient::HidlImpl::config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector>* const failures) { c2_hidl::Params hidlParams; if (!c2_hidl::utils::createParamsBlob(&hidlParams, params)) { LOG(ERROR) << "config -- bad input."; return C2_TRANSACTION_FAILED; } c2_status_t status; Return transStatus = mBase->config( hidlParams, mayBlock == C2_MAY_BLOCK, [&status, ¶ms, failures]( c2_hidl::Status s, const hidl_vec f, const c2_hidl::Params& o) { status = static_cast(s); if (status != C2_OK && status != C2_BAD_INDEX) { LOG(DEBUG) << "config -- call failed: " << status << "."; } size_t i = failures->size(); failures->resize(i + f.size()); for (const c2_hidl::SettingResult& sf : f) { if (!c2_hidl::utils::objcpy(&(*failures)[i++], sf)) { LOG(ERROR) << "config -- " << "invalid SettingResult returned."; return; } } if (!c2_hidl::utils::updateParamsFromBlob(params, o)) { LOG(ERROR) << "config -- " << "failed to parse returned params."; status = C2_CORRUPTED; } }); if (!transStatus.isOk()) { LOG(ERROR) << "config -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; } c2_status_t Codec2ConfigurableClient::HidlImpl::querySupportedParams( std::vector>* const params) const { // TODO: Cache and query properly! c2_status_t status; Return transStatus = mBase->querySupportedParams( std::numeric_limits::min(), std::numeric_limits::max(), [&status, params]( c2_hidl::Status s, const hidl_vec& p) { status = static_cast(s); if (status != C2_OK) { LOG(DEBUG) << "querySupportedParams -- call failed: " << status << "."; return; } size_t i = params->size(); params->resize(i + p.size()); for (const c2_hidl::ParamDescriptor& sp : p) { if (!c2_hidl::utils::objcpy(&(*params)[i++], sp)) { LOG(ERROR) << "querySupportedParams -- " << "invalid returned ParamDescriptor."; return; } } }); if (!transStatus.isOk()) { LOG(ERROR) << "querySupportedParams -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; } c2_status_t Codec2ConfigurableClient::HidlImpl::querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const { hidl_vec inFields(fields.size()); for (size_t i = 0; i < fields.size(); ++i) { if (!c2_hidl::utils::objcpy(&inFields[i], fields[i])) { LOG(ERROR) << "querySupportedValues -- bad input"; return C2_TRANSACTION_FAILED; } } c2_status_t status; Return transStatus = mBase->querySupportedValues( inFields, mayBlock == C2_MAY_BLOCK, [&status, &inFields, &fields]( c2_hidl::Status s, const hidl_vec& r) { status = static_cast(s); if (status != C2_OK) { LOG(DEBUG) << "querySupportedValues -- call failed: " << status << "."; return; } if (r.size() != fields.size()) { LOG(ERROR) << "querySupportedValues -- " "input and output lists " "have different sizes."; status = C2_CORRUPTED; return; } for (size_t i = 0; i < fields.size(); ++i) { if (!c2_hidl::utils::objcpy(&fields[i], inFields[i], r[i])) { LOG(ERROR) << "querySupportedValues -- " "invalid returned value."; status = C2_CORRUPTED; return; } } }); if (!transStatus.isOk()) { LOG(ERROR) << "querySupportedValues -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; } // Codec2ConfigurableClient::AidlImpl struct Codec2ConfigurableClient::AidlImpl : public Codec2ConfigurableClient::ImplBase { typedef c2_aidl::IConfigurable Base; // base cannot be null. explicit AidlImpl(const std::shared_ptr& base); const C2String& getName() const override { return mName; } c2_status_t query( const std::vector& stackParams, const std::vector &heapParamIndices, c2_blocking_t mayBlock, std::vector>* const heapParams) const override; c2_status_t config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector>* const failures) override; c2_status_t querySupportedParams( std::vector>* const params ) const override; c2_status_t querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const override; private: std::shared_ptr mBase; const C2String mName; }; Codec2ConfigurableClient::AidlImpl::AidlImpl(const std::shared_ptr& base) : mBase{base}, mName{[base]() -> C2String { std::string outName; ndk::ScopedAStatus status = base->getName(&outName); return status.isOk() ? outName : ""; }()} { } c2_status_t Codec2ConfigurableClient::AidlImpl::query( const std::vector &stackParams, const std::vector &heapParamIndices, c2_blocking_t mayBlock, std::vector>* const heapParams) const { std::vector indices( stackParams.size() + heapParamIndices.size()); size_t numIndices = 0; for (C2Param* const& stackParam : stackParams) { if (!stackParam) { LOG(WARNING) << "query -- null stack param encountered."; continue; } indices[numIndices++] = int(stackParam->index()); } size_t numStackIndices = numIndices; for (const C2Param::Index& index : heapParamIndices) { indices[numIndices++] = int(static_cast(index)); } indices.resize(numIndices); if (heapParams) { heapParams->reserve(heapParams->size() + numIndices); } c2_aidl::IConfigurable::QueryResult result; ndk::ScopedAStatus transStatus = mBase->query(indices, (mayBlock == C2_MAY_BLOCK), &result); c2_status_t status = GetC2Status(transStatus, "query"); if (status != C2_OK) { return status; } status = static_cast(result.status.status); std::vector paramPointers; if (!c2_aidl::utils::ParseParamsBlob(¶mPointers, result.params)) { LOG(ERROR) << "query -- error while parsing params."; return C2_CORRUPTED; } size_t i = 0; size_t numQueried = 0; for (auto it = paramPointers.begin(); it != paramPointers.end(); ) { C2Param* paramPointer = *it; if (numStackIndices > 0) { --numStackIndices; if (!paramPointer) { LOG(DEBUG) << "query -- null stack param."; ++it; continue; } for (; i < stackParams.size() && !stackParams[i]; ) { ++i; } if (i >= stackParams.size()) { LOG(ERROR) << "query -- unexpected error."; status = C2_CORRUPTED; break; } if (stackParams[i]->index() != paramPointer->index()) { LOG(DEBUG) << "query -- param skipped: " "index = " << stackParams[i]->index() << "."; stackParams[i++]->invalidate(); // this means that the param could not be queried. // signalling C2_BAD_INDEX to the client. status = C2_BAD_INDEX; continue; } if (stackParams[i++]->updateFrom(*paramPointer)) { ++numQueried; } else { LOG(WARNING) << "query -- param update failed: " "index = " << paramPointer->index() << "."; } } else { if (!paramPointer) { LOG(DEBUG) << "query -- null heap param."; ++it; continue; } if (!heapParams) { LOG(WARNING) << "query -- " "unexpected extra stack param."; } else { heapParams->emplace_back(C2Param::Copy(*paramPointer)); ++numQueried; } } ++it; } if (status == C2_OK && indices.size() != numQueried) { status = C2_BAD_INDEX; } return status; } c2_status_t Codec2ConfigurableClient::AidlImpl::config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector>* const failures) { c2_aidl::Params aidlParams; if (!c2_aidl::utils::CreateParamsBlob(&aidlParams, params)) { LOG(ERROR) << "config -- bad input."; return C2_TRANSACTION_FAILED; } c2_aidl::IConfigurable::ConfigResult result; ndk::ScopedAStatus transStatus = mBase->config(aidlParams, (mayBlock == C2_MAY_BLOCK), &result); c2_status_t status = GetC2Status(transStatus, "config"); if (status != C2_OK) { return status; } status = static_cast(result.status.status); size_t i = failures->size(); failures->resize(i + result.failures.size()); for (const c2_aidl::SettingResult& sf : result.failures) { if (!c2_aidl::utils::FromAidl(&(*failures)[i++], sf)) { LOG(ERROR) << "config -- invalid SettingResult returned."; return C2_CORRUPTED; } } if (!c2_aidl::utils::UpdateParamsFromBlob(params, result.params)) { LOG(ERROR) << "config -- " << "failed to parse returned params."; status = C2_CORRUPTED; } return status; } c2_status_t Codec2ConfigurableClient::AidlImpl::querySupportedParams( std::vector>* const params) const { // TODO: Cache and query properly! std::vector result; ndk::ScopedAStatus transStatus = mBase->querySupportedParams( std::numeric_limits::min(), std::numeric_limits::max(), &result); c2_status_t status = GetC2Status(transStatus, "querySupportedParams"); if (status != C2_OK) { return status; } size_t i = params->size(); params->resize(i + result.size()); for (const c2_aidl::ParamDescriptor& sp : result) { if (!c2_aidl::utils::FromAidl(&(*params)[i++], sp)) { LOG(ERROR) << "querySupportedParams -- invalid returned ParamDescriptor."; return C2_CORRUPTED; } } return status; } c2_status_t Codec2ConfigurableClient::AidlImpl::querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const { std::vector inFields(fields.size()); for (size_t i = 0; i < fields.size(); ++i) { if (!c2_aidl::utils::ToAidl(&inFields[i], fields[i])) { LOG(ERROR) << "querySupportedValues -- bad input"; return C2_TRANSACTION_FAILED; } } c2_aidl::IConfigurable::QuerySupportedValuesResult result; ndk::ScopedAStatus transStatus = mBase->querySupportedValues( inFields, (mayBlock == C2_MAY_BLOCK), &result); c2_status_t status = GetC2Status(transStatus, "querySupportedValues"); if (status != C2_OK) { return status; } status = static_cast(result.status.status); if (result.values.size() != fields.size()) { LOG(ERROR) << "querySupportedValues -- " "input and output lists " "have different sizes."; return C2_CORRUPTED; } for (size_t i = 0; i < fields.size(); ++i) { if (!c2_aidl::utils::FromAidl(&fields[i], inFields[i], result.values[i])) { LOG(ERROR) << "querySupportedValues -- " "invalid returned value."; return C2_CORRUPTED; } } return status; } // Codec2ConfigurableClient Codec2ConfigurableClient::Codec2ConfigurableClient(const sp &hidlBase) : mImpl(new Codec2ConfigurableClient::HidlImpl(hidlBase)) { } Codec2ConfigurableClient::Codec2ConfigurableClient( const std::shared_ptr &aidlBase) : mImpl(new Codec2ConfigurableClient::AidlImpl(aidlBase)) { } const C2String& Codec2ConfigurableClient::getName() const { return mImpl->getName(); } c2_status_t Codec2ConfigurableClient::query( const std::vector& stackParams, const std::vector &heapParamIndices, c2_blocking_t mayBlock, std::vector>* const heapParams) const { return mImpl->query(stackParams, heapParamIndices, mayBlock, heapParams); } c2_status_t Codec2ConfigurableClient::config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector>* const failures) { return mImpl->config(params, mayBlock, failures); } c2_status_t Codec2ConfigurableClient::querySupportedParams( std::vector>* const params) const { return mImpl->querySupportedParams(params); } c2_status_t Codec2ConfigurableClient::querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const { return mImpl->querySupportedValues(fields, mayBlock); } // Codec2Client::Component::HidlListener struct Codec2Client::Component::HidlListener : public c2_hidl::IComponentListener { std::weak_ptr component; std::weak_ptr base; virtual Return onWorkDone(const c2_hidl::WorkBundle& workBundle) override { std::list> workItems; if (!c2_hidl::utils::objcpy(&workItems, workBundle)) { LOG(DEBUG) << "onWorkDone -- received corrupted WorkBundle."; return Void(); } // release input buffers potentially held by the component from queue std::shared_ptr strongComponent = component.lock(); if (strongComponent) { strongComponent->handleOnWorkDone(workItems); } if (std::shared_ptr listener = base.lock()) { listener->onWorkDone(component, workItems); } else { LOG(DEBUG) << "onWorkDone -- listener died."; } return Void(); } virtual Return onTripped( const hidl_vec& settingResults) override { std::vector> c2SettingResults( settingResults.size()); for (size_t i = 0; i < settingResults.size(); ++i) { std::unique_ptr c2SettingResult; if (!c2_hidl::utils::objcpy(&c2SettingResult, settingResults[i])) { LOG(DEBUG) << "onTripped -- received corrupted SettingResult."; return Void(); } c2SettingResults[i] = std::move(c2SettingResult); } if (std::shared_ptr listener = base.lock()) { listener->onTripped(component, c2SettingResults); } else { LOG(DEBUG) << "onTripped -- listener died."; } return Void(); } virtual Return onError(c2_hidl::Status s, uint32_t errorCode) override { LOG(DEBUG) << "onError --" << " status = " << s << ", errorCode = " << errorCode << "."; if (std::shared_ptr listener = base.lock()) { listener->onError(component, s == c2_hidl::Status::OK ? errorCode : static_cast(s)); } else { LOG(DEBUG) << "onError -- listener died."; } return Void(); } virtual Return onFramesRendered( const hidl_vec& renderedFrames) override { std::shared_ptr listener = base.lock(); if (!listener) { LOG(DEBUG) << "onFramesRendered -- listener died."; return Void(); } for (const RenderedFrame& renderedFrame : renderedFrames) { listener->onFrameRendered( renderedFrame.bufferQueueId, renderedFrame.slotId, renderedFrame.timestampNs); } return Void(); } virtual Return onInputBuffersReleased( const hidl_vec& inputBuffers) override { std::shared_ptr listener = base.lock(); if (!listener) { LOG(DEBUG) << "onInputBuffersReleased -- listener died."; return Void(); } for (const InputBuffer& inputBuffer : inputBuffers) { LOG(VERBOSE) << "onInputBuffersReleased --" " received death notification of" " input buffer:" " frameIndex = " << inputBuffer.frameIndex << ", bufferIndex = " << inputBuffer.arrayIndex << "."; listener->onInputBufferDone( inputBuffer.frameIndex, inputBuffer.arrayIndex); } return Void(); } }; // Codec2Client::Component::AidlListener struct Codec2Client::Component::AidlListener : public c2_aidl::BnComponentListener { std::weak_ptr component; std::weak_ptr base; virtual ::ndk::ScopedAStatus onWorkDone(const c2_aidl::WorkBundle& workBundle) override { std::list> workItems; if (!c2_aidl::utils::FromAidl(&workItems, workBundle)) { LOG(DEBUG) << "onWorkDone -- received corrupted WorkBundle."; return ::ndk::ScopedAStatus::ok(); } // release input buffers potentially held by the component from queue std::shared_ptr strongComponent = component.lock(); if (strongComponent) { strongComponent->handleOnWorkDone(workItems); } if (std::shared_ptr listener = base.lock()) { listener->onWorkDone(component, workItems); } else { LOG(DEBUG) << "onWorkDone -- listener died."; } return ::ndk::ScopedAStatus::ok(); } virtual ::ndk::ScopedAStatus onTripped( const std::vector& settingResults) override { std::vector> c2SettingResults( settingResults.size()); for (size_t i = 0; i < settingResults.size(); ++i) { std::unique_ptr c2SettingResult; if (!c2_aidl::utils::FromAidl(&c2SettingResult, settingResults[i])) { LOG(DEBUG) << "onTripped -- received corrupted SettingResult."; return ::ndk::ScopedAStatus::ok(); } c2SettingResults[i] = std::move(c2SettingResult); } if (std::shared_ptr listener = base.lock()) { listener->onTripped(component, c2SettingResults); } else { LOG(DEBUG) << "onTripped -- listener died."; } return ::ndk::ScopedAStatus::ok(); } virtual ::ndk::ScopedAStatus onError(const c2_aidl::Status &s, int32_t errorCode) override { LOG(DEBUG) << "onError --" << " status = " << s.status << ", errorCode = " << errorCode << "."; if (std::shared_ptr listener = base.lock()) { listener->onError(component, s.status == c2_aidl::Status::OK ? errorCode : static_cast(s.status)); } else { LOG(DEBUG) << "onError -- listener died."; } return ::ndk::ScopedAStatus::ok(); } virtual ::ndk::ScopedAStatus onFramesRendered( const std::vector& renderedFrames) override { std::shared_ptr listener = base.lock(); if (!listener) { LOG(DEBUG) << "onFramesRendered -- listener died."; return ::ndk::ScopedAStatus::ok(); } for (const RenderedFrame& renderedFrame : renderedFrames) { listener->onFrameRendered( renderedFrame.bufferQueueId, renderedFrame.slotId, renderedFrame.timestampNs); } return ::ndk::ScopedAStatus::ok(); } virtual ::ndk::ScopedAStatus onInputBuffersReleased( const std::vector& inputBuffers) override { std::shared_ptr listener = base.lock(); if (!listener) { LOG(DEBUG) << "onInputBuffersReleased -- listener died."; return ::ndk::ScopedAStatus::ok(); } for (const InputBuffer& inputBuffer : inputBuffers) { LOG(VERBOSE) << "onInputBuffersReleased --" " received death notification of" " input buffer:" " frameIndex = " << inputBuffer.frameIndex << ", bufferIndex = " << inputBuffer.arrayIndex << "."; listener->onInputBufferDone( inputBuffer.frameIndex, inputBuffer.arrayIndex); } return ::ndk::ScopedAStatus::ok(); } }; // Codec2Client::Component::HidlBufferPoolSender struct Codec2Client::Component::HidlBufferPoolSender : hardware::media::c2::V1_1::utils::DefaultBufferPoolSender { HidlBufferPoolSender() : hardware::media::c2::V1_1::utils::DefaultBufferPoolSender() { } }; // Codec2Client::Component::AidlBufferPoolSender struct Codec2Client::Component::AidlBufferPoolSender : c2_aidl::utils::DefaultBufferPoolSender { AidlBufferPoolSender() : c2_aidl::utils::DefaultBufferPoolSender() { } }; // Codec2Client::Component::OutputBufferQueue struct Codec2Client::Component::OutputBufferQueue : hardware::media::c2::OutputBufferQueue { OutputBufferQueue() : hardware::media::c2::OutputBufferQueue() { } }; // The class holds GraphicBufferAllocator and the associated id of // HAL side BlockPool. // This is tightly coupled with BlockPool creation and destruction. // The life cycle inside class will be as follows. // // On createBlockPool client request. // 1. this::create() creates a GraphicBufferAllocator and set it as // the current. // 2. C2AIDL_HAL::createBlockPool() creates a C2BlockPool using // the GraphicBufferAllocator created in #1. // 3. this::setCurrentId() associates the id returned in #2 to the current // // On destroyBlockPool cliet request // 1. C2AIDL_HAL::destroyBlockPool() destroys the block pool // from HAL process. // 2. this::remove() destroys GraphicBufferAllocator which is associatted // with the C2BlockPool in #1. // struct Codec2Client::Component::GraphicBufferAllocators { private: std::optional mCurrentId; std::shared_ptr mCurrent; // A new BlockPool is created before the old BlockPool is destroyed. // This holds the reference of the old BlockPool when a new BlockPool is // created until the old BlockPool is explicitly requested for destruction. std::map> mOlds; std::mutex mMutex; public: // Creates a GraphicBufferAllocator which will be passed to HAL // for creating C2BlockPool. And the created GraphicBufferAllocator // will be used afterwards by current(). std::shared_ptr create() { std::unique_lock l(mMutex); if (mCurrent) { // If this is not stopped. mCurrent->reset(); if (mCurrentId.has_value()) { mOlds.emplace(mCurrentId.value(), mCurrent); } mCurrentId.reset(); mCurrent.reset(); } // TODO: integrate initial value with CCodec/CCodecBufferChannel mCurrent = AidlGraphicBufferAllocator::CreateGraphicBufferAllocator(3 /* maxDequeueCount */); ALOGD("GraphicBufferAllocator created"); return mCurrent; } // Associates the blockpool Id returned from HAL to the // current GraphicBufferAllocator. void setCurrentId(C2BlockPool::local_id_t id) { std::unique_lock l(mMutex); CHECK(!mCurrentId.has_value()); mCurrentId = id; } // Returns the current GraphicBufferAllocator. std::shared_ptr current() { std::unique_lock l(mMutex); return mCurrent; } // Removes the GraphicBufferAllocator associated with given \p id. void remove(C2BlockPool::local_id_t id) { std::unique_lock l(mMutex); mOlds.erase(id); if (mCurrentId == id) { if (mCurrent) { mCurrent->reset(); mCurrent.reset(); } mCurrentId.reset(); } } }; // Codec2Client Codec2Client::Codec2Client(sp const& base, sp const& configurable, size_t serviceIndex) : Configurable{configurable}, mHidlBase1_0{base}, mHidlBase1_1{HidlBase1_1::castFrom(base)}, mHidlBase1_2{HidlBase1_2::castFrom(base)}, mServiceIndex{serviceIndex} { Return> transResult = base->getPoolClientManager(); if (!transResult.isOk()) { LOG(ERROR) << "getPoolClientManager -- transaction failed."; } else { mHidlHostPoolManager = static_cast>(transResult); } } Codec2Client::Codec2Client(std::shared_ptr const& base, std::shared_ptr const& configurable, size_t serviceIndex) : Configurable{configurable}, mAidlBase{base}, mServiceIndex{serviceIndex} { ::ndk::ScopedAStatus transStatus = base->getPoolClientManager(&mAidlHostPoolManager); if (!transStatus.isOk()) { LOG(ERROR) << "getPoolClientManager -- transaction failed."; mAidlHostPoolManager.reset(); } } sp const& Codec2Client::getHidlBase() const { return mHidlBase1_0; } sp const& Codec2Client::getHidlBase1_0() const { return mHidlBase1_0; } sp const& Codec2Client::getHidlBase1_1() const { return mHidlBase1_1; } sp const& Codec2Client::getHidlBase1_2() const { return mHidlBase1_2; } ::ndk::SpAIBinder Codec2Client::getAidlBase() const { return mAidlBase ? mAidlBase->asBinder() : nullptr; } std::string const& Codec2Client::getServiceName() const { return GetServiceNames()[mServiceIndex]; } c2_status_t Codec2Client::createComponent( const C2String& name, const std::shared_ptr& listener, std::shared_ptr* const component) { if (mAidlBase) { std::shared_ptr aidlListener = Component::AidlListener::make(); aidlListener->base = listener; std::shared_ptr aidlComponent; ::ndk::ScopedAStatus transStatus = mAidlBase->createComponent( name, aidlListener, bufferpool2_aidl::implementation::ClientManager::getInstance(), &aidlComponent); c2_status_t status = GetC2Status(transStatus, "createComponent"); if (status != C2_OK) { return status; } else if (!aidlComponent) { LOG(ERROR) << "createComponent(" << name.c_str() << ") -- null component."; return C2_CORRUPTED; } *component = std::make_shared(aidlComponent); status = (*component)->setDeathListener((*component), listener); if (status != C2_OK) { LOG(ERROR) << "createComponent(" << name.c_str() << ") -- failed to set up death listener: " << status << "."; } (*component)->mAidlBufferPoolSender->setReceiver(mAidlHostPoolManager); aidlListener->component = *component; return status; } c2_status_t status; sp hidlListener = new Component::HidlListener{}; hidlListener->base = listener; Return transStatus; if (mHidlBase1_2) { transStatus = mHidlBase1_2->createComponent_1_2( name, hidlListener, bufferpool_hidl::implementation::ClientManager::getInstance(), [&status, component, hidlListener]( c2_hidl::Status s, const sp& c) { status = static_cast(s); if (status != C2_OK) { return; } *component = std::make_shared(c); hidlListener->component = *component; }); } else if (mHidlBase1_1) { transStatus = mHidlBase1_1->createComponent_1_1( name, hidlListener, bufferpool_hidl::implementation::ClientManager::getInstance(), [&status, component, hidlListener]( c2_hidl::Status s, const sp& c) { status = static_cast(s); if (status != C2_OK) { return; } *component = std::make_shared(c); hidlListener->component = *component; }); } else if (mHidlBase1_0) { // ver1_0 transStatus = mHidlBase1_0->createComponent( name, hidlListener, bufferpool_hidl::implementation::ClientManager::getInstance(), [&status, component, hidlListener]( c2_hidl::Status s, const sp& c) { status = static_cast(s); if (status != C2_OK) { return; } *component = std::make_shared(c); hidlListener->component = *component; }); } else { status = C2_CORRUPTED; } if (!transStatus.isOk()) { LOG(ERROR) << "createComponent(" << name.c_str() << ") -- transaction failed."; return C2_TRANSACTION_FAILED; } else if (status != C2_OK) { if (status == C2_NOT_FOUND) { LOG(VERBOSE) << "createComponent(" << name.c_str() << ") -- component not found."; } else { LOG(ERROR) << "createComponent(" << name.c_str() << ") -- call failed: " << status << "."; } return status; } else if (!*component) { LOG(ERROR) << "createComponent(" << name.c_str() << ") -- null component."; return C2_CORRUPTED; } status = (*component)->setDeathListener(*component, listener); if (status != C2_OK) { LOG(ERROR) << "createComponent(" << name.c_str() << ") -- failed to set up death listener: " << status << "."; } (*component)->mHidlBufferPoolSender->setReceiver(mHidlHostPoolManager); return status; } c2_status_t Codec2Client::createInterface( const C2String& name, std::shared_ptr* const interface) { if (mAidlBase) { std::shared_ptr aidlInterface; ::ndk::ScopedAStatus transStatus = mAidlBase->createInterface( name, &aidlInterface); c2_status_t status = GetC2Status(transStatus, "createInterface"); if (status != C2_OK) { return status; } else if (!aidlInterface) { LOG(ERROR) << "createInterface(" << name.c_str() << ") -- null interface."; return C2_CORRUPTED; } interface->reset(new Codec2Client::Interface(aidlInterface)); return C2_OK; } c2_status_t status; Return transStatus = mHidlBase1_0->createInterface( name, [&status, interface]( c2_hidl::Status s, const sp& i) { status = static_cast(s); if (status != C2_OK) { return; } *interface = std::make_shared(i); }); if (!transStatus.isOk()) { LOG(ERROR) << "createInterface(" << name.c_str() << ") -- transaction failed."; return C2_TRANSACTION_FAILED; } else if (status != C2_OK) { if (status == C2_NOT_FOUND) { LOG(VERBOSE) << "createInterface(" << name.c_str() << ") -- component not found."; } else { LOG(ERROR) << "createInterface(" << name.c_str() << ") -- call failed: " << status << "."; } return status; } return status; } c2_status_t Codec2Client::createInputSurface( std::shared_ptr* const inputSurface) { if (mAidlBase) { // FIXME return C2_OMITTED; } c2_status_t status; Return transStatus = mHidlBase1_0->createInputSurface( [&status, inputSurface]( c2_hidl::Status s, const sp& i) { status = static_cast(s); if (status != C2_OK) { return; } *inputSurface = std::make_shared(i); }); if (!transStatus.isOk()) { LOG(ERROR) << "createInputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; } else if (status != C2_OK) { LOG(DEBUG) << "createInputSurface -- call failed: " << status << "."; } return status; } std::vector const& Codec2Client::listComponents() const { return Cache::List()[mServiceIndex].getTraits(); } std::vector Codec2Client::_listComponents( bool* success) const { std::vector traits; std::string const& serviceName = getServiceName(); if (mAidlBase) { std::vector aidlTraits; ::ndk::ScopedAStatus transStatus = mAidlBase->listComponents(&aidlTraits); if (!transStatus.isOk()) { LOG(ERROR) << "_listComponents -- transaction failed."; *success = false; } else { traits.resize(aidlTraits.size()); *success = true; for (size_t i = 0; i < aidlTraits.size(); ++i) { if (!c2_aidl::utils::FromAidl(&traits[i], aidlTraits[i])) { LOG(ERROR) << "_listComponents -- corrupted output."; *success = false; traits.clear(); break; } traits[i].owner = serviceName; } } return traits; } Return transStatus = mHidlBase1_0->listComponents( [&traits, &serviceName](c2_hidl::Status s, const hidl_vec& t) { if (s != c2_hidl::Status::OK) { LOG(DEBUG) << "_listComponents -- call failed: " << static_cast(s) << "."; return; } traits.resize(t.size()); for (size_t i = 0; i < t.size(); ++i) { if (!c2_hidl::utils::objcpy(&traits[i], t[i])) { LOG(ERROR) << "_listComponents -- corrupted output."; return; } traits[i].owner = serviceName; } }); if (!transStatus.isOk()) { LOG(ERROR) << "_listComponents -- transaction failed."; *success = false; } else { *success = true; } return traits; } c2_status_t Codec2Client::copyBuffer( const std::shared_ptr& src, const std::shared_ptr& dst) { // TODO: Implement? (void)src; (void)dst; LOG(ERROR) << "copyBuffer not implemented"; return C2_OMITTED; } std::shared_ptr Codec2Client::getParamReflector() { // TODO: this is not meant to be exposed as C2ParamReflector on the client side; instead, it // should reflect the HAL API. struct HidlSimpleParamReflector : public C2ParamReflector { std::unique_ptr describe( C2Param::CoreIndex coreIndex) const override { hidl_vec indices(1); indices[0] = static_cast(coreIndex.coreIndex()); std::unique_ptr descriptor; Return transStatus = mBase->getStructDescriptors( indices, [&descriptor]( c2_hidl::Status s, const hidl_vec& sd) { c2_status_t status = static_cast(s); if (status != C2_OK) { LOG(DEBUG) << "SimpleParamReflector -- " "getStructDescriptors() failed: " << status << "."; descriptor.reset(); return; } if (sd.size() != 1) { LOG(DEBUG) << "SimpleParamReflector -- " "getStructDescriptors() " "returned vector of size " << sd.size() << ". " "It should be 1."; descriptor.reset(); return; } if (!c2_hidl::utils::objcpy(&descriptor, sd[0])) { LOG(DEBUG) << "SimpleParamReflector -- " "getStructDescriptors() returned " "corrupted data."; descriptor.reset(); return; } }); if (!transStatus.isOk()) { LOG(DEBUG) << "SimpleParamReflector -- transaction failed: " << transStatus.description(); descriptor.reset(); } return descriptor; } HidlSimpleParamReflector(sp base) : mBase(base) { } sp mBase; }; struct AidlSimpleParamReflector : public C2ParamReflector { std::unique_ptr describe( C2Param::CoreIndex coreIndex) const override { std::vector aidlDesc; std::unique_ptr descriptor; ::ndk::ScopedAStatus transStatus = mBase->getStructDescriptors( {int32_t(coreIndex.coreIndex())}, &aidlDesc); c2_status_t status = GetC2Status(transStatus, "describe"); if (status != C2_OK) { descriptor.reset(); } else if (!c2_aidl::utils::FromAidl(&descriptor, aidlDesc[0])) { LOG(ERROR) << "describe -- conversion failed."; descriptor.reset(); } return descriptor; } AidlSimpleParamReflector(const std::shared_ptr &base) : mBase(base) { } std::shared_ptr mBase; }; if (mAidlBase) { return std::make_shared(mAidlBase); } return std::make_shared(mHidlBase1_0); }; std::vector Codec2Client::CacheServiceNames() { std::vector names; if (c2_aidl::utils::IsSelected()) { if (__builtin_available(android __ANDROID_API_S__, *)) { // Get AIDL service names AServiceManager_forEachDeclaredInstance( AidlBase::descriptor, &names, [](const char *name, void *context) { std::vector *names = (std::vector *)context; names->emplace_back(name); }); } else { LOG(FATAL) << "C2 AIDL cannot be selected on Android version older than 35"; } } else { // Get HIDL service names using ::android::hardware::media::c2::V1_0::IComponentStore; using ::android::hidl::manager::V1_2::IServiceManager; while (true) { sp serviceManager = IServiceManager::getService(); CHECK(serviceManager) << "Hardware service manager is not running."; Return transResult; transResult = serviceManager->listManifestByInterface( IComponentStore::descriptor, [&names]( hidl_vec const& instanceNames) { names.insert(names.end(), instanceNames.begin(), instanceNames.end()); }); if (transResult.isOk()) { break; } LOG(ERROR) << "Could not retrieve the list of service instances of " << IComponentStore::descriptor << ". Retrying..."; } } // Sort service names in each category. std::stable_sort( names.begin(), names.end(), [](const std::string &a, const std::string &b) { // First compare by prefix: default -> vendor -> {everything else} constexpr int DEFAULT = 1; constexpr int VENDOR = 2; constexpr int OTHER = 3; int aPrefix = ((a.compare(0, 7, "default") == 0) ? DEFAULT : (a.compare(0, 6, "vendor") == 0) ? VENDOR : OTHER); int bPrefix = ((b.compare(0, 7, "default") == 0) ? DEFAULT : (b.compare(0, 6, "vendor") == 0) ? VENDOR : OTHER); if (aPrefix != bPrefix) { return aPrefix < bPrefix; } // If the prefix is the same, compare alphabetically return a < b; }); // Summarize to logcat. if (names.empty()) { LOG(INFO) << "No Codec2 services declared in the manifest."; } else { std::stringstream stringOutput; stringOutput << "Available Codec2 services:"; for (std::string const& name : names) { stringOutput << " \"" << name << "\""; } LOG(INFO) << stringOutput.str(); } return names; } std::vector const& Codec2Client::GetServiceNames() { static std::vector sServiceNames = CacheServiceNames(); return sServiceNames; } std::shared_ptr Codec2Client::CreateFromService( const char* name, bool setAsPreferredCodec2ComponentStore) { size_t index = getServiceIndex(name); if (index == GetServiceNames().size()) { if (setAsPreferredCodec2ComponentStore) { LOG(WARNING) << "CreateFromService(" << name << ") -- preferred C2ComponentStore not set."; } return nullptr; } std::shared_ptr client = _CreateFromIndex(index); if (setAsPreferredCodec2ComponentStore) { SetPreferredCodec2ComponentStore( std::make_shared(client)); LOG(INFO) << "CreateFromService(" << name << ") -- service set as preferred C2ComponentStore."; } return client; } std::vector> Codec2Client:: CreateFromAllServices() { std::vector> clients( GetServiceNames().size()); for (size_t i = GetServiceNames().size(); i > 0; ) { --i; clients[i] = _CreateFromIndex(i); } return clients; } std::shared_ptr Codec2Client::_CreateFromIndex(size_t index) { std::string const& name = GetServiceNames()[index]; LOG(VERBOSE) << "Creating a Codec2 client to service \"" << name << "\""; if (c2_aidl::utils::IsSelected()) { if (__builtin_available(android __ANDROID_API_S__, *)) { std::string instanceName = ::android::base::StringPrintf("%s/%s", AidlBase::descriptor, name.c_str()); if (AServiceManager_isDeclared(instanceName.c_str())) { std::shared_ptr baseStore = AidlBase::fromBinder( ::ndk::SpAIBinder(AServiceManager_waitForService(instanceName.c_str()))); CHECK(baseStore) << "Codec2 AIDL service \"" << name << "\"" " inaccessible for unknown reasons."; LOG(VERBOSE) << "Client to Codec2 AIDL service \"" << name << "\" created"; std::shared_ptr configurable; ::ndk::ScopedAStatus transStatus = baseStore->getConfigurable(&configurable); CHECK(transStatus.isOk()) << "Codec2 AIDL service \"" << name << "\"" "does not have IConfigurable."; return std::make_shared(baseStore, configurable, index); } else { LOG(ERROR) << "Codec2 AIDL service \"" << name << "\" is not declared"; } } else { LOG(FATAL) << "C2 AIDL cannot be selected on Android version older than 35"; } } else { std::string instanceName = "android.hardware.media.c2/" + name; sp baseStore = HidlBase::getService(name); CHECK(baseStore) << "Codec2 service \"" << name << "\"" " inaccessible for unknown reasons."; LOG(VERBOSE) << "Client to Codec2 service \"" << name << "\" created"; Return> transResult = baseStore->getConfigurable(); CHECK(transResult.isOk()) << "Codec2 service \"" << name << "\"" "does not have IConfigurable."; sp configurable = static_cast>(transResult); return std::make_shared(baseStore, configurable, index); } return nullptr; } c2_status_t Codec2Client::ForAllServices( const std::string &key, size_t numberOfAttempts, std::function&)> predicate) { c2_status_t status = C2_NO_INIT; // no IComponentStores present // Cache the mapping key -> index of Codec2Client in Cache::List(). static std::mutex key2IndexMutex; static std::map 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 indices; for (size_t index = Cache::List().size(); index > 0; ) { indices.push_front(--index); } bool wasMapped = false; { std::scoped_lock lock{key2IndexMutex}; auto it = key2Index.find(key); if (it != key2Index.end()) { indices.push_front(it->second); wasMapped = true; } } for (size_t index : indices) { Cache& cache = Cache::List()[index]; for (size_t tries = numberOfAttempts; tries > 0; --tries) { std::shared_ptr client{cache.getClient()}; status = predicate(client); if (status == C2_OK) { std::scoped_lock lock{key2IndexMutex}; key2Index[key] = index; // update last known client index return C2_OK; } else if (status == C2_NO_MEMORY) { return C2_NO_MEMORY; } else if (status == C2_TRANSACTION_FAILED) { LOG(WARNING) << "\"" << key << "\" failed for service \"" << client->getName() << "\" due to transaction failure. " << "(Service may have crashed.)" << (tries > 1 ? " Retrying..." : ""); cache.invalidate(); continue; } if (wasMapped) { LOG(INFO) << "\"" << key << "\" became invalid in service \"" << client->getName() << "\". Retrying..."; wasMapped = false; } break; } } return status; // return the last status from a valid client } c2_status_t Codec2Client::CreateComponentByName( const char* componentName, const std::shared_ptr& listener, std::shared_ptr* component, std::shared_ptr* owner, size_t numberOfAttempts) { std::string key{"create:"}; key.append(componentName); c2_status_t status = ForAllServices( key, numberOfAttempts, [owner, component, componentName, &listener]( const std::shared_ptr &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) { LOG(DEBUG) << "IComponentStore(" << client->getServiceName() << ")::createComponent(\"" << componentName << "\") returned status = " << status << "."; } return status; }); if (status != C2_OK) { LOG(DEBUG) << "Failed to create component \"" << componentName << "\" from all known services. " "Last returned status = " << status << "."; } return status; } std::shared_ptr Codec2Client::CreateInterfaceByName( const char* interfaceName, std::shared_ptr* owner, size_t numberOfAttempts) { std::string key{"create:"}; key.append(interfaceName); std::shared_ptr interface; c2_status_t status = ForAllServices( key, numberOfAttempts, [owner, &interface, interfaceName]( const std::shared_ptr &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) { LOG(DEBUG) << "IComponentStore(" << client->getServiceName() << ")::createInterface(\"" << interfaceName << "\") returned status = " << status << "."; } return status; }); if (status != C2_OK) { LOG(DEBUG) << "Failed to create interface \"" << interfaceName << "\" from all known services. " "Last returned status = " << status << "."; } return interface; } std::vector const& Codec2Client::ListComponents() { static std::vector sList{[]() { std::vector list; for (Cache& cache : Cache::List()) { std::vector const& traits = cache.getTraits(); list.insert(list.end(), traits.begin(), traits.end()); } return list; }()}; return sList; } std::shared_ptr Codec2Client::CreateInputSurface( char const* serviceName) { int32_t inputSurfaceSetting = ::android::base::GetIntProperty( "debug.stagefright.c2inputsurface", int32_t(0)); if (inputSurfaceSetting <= 0) { return nullptr; } size_t index = GetServiceNames().size(); if (serviceName) { index = getServiceIndex(serviceName); if (index == GetServiceNames().size()) { LOG(DEBUG) << "CreateInputSurface -- invalid service name: \"" << serviceName << "\""; } } std::shared_ptr inputSurface; if (index != GetServiceNames().size()) { std::shared_ptr client = Cache::List()[index].getClient(); if (client->createInputSurface(&inputSurface) == C2_OK) { return inputSurface; } } LOG(INFO) << "CreateInputSurface -- attempting to create an input surface " "from all services..."; for (Cache& cache : Cache::List()) { std::shared_ptr client = cache.getClient(); if (client->createInputSurface(&inputSurface) == C2_OK) { LOG(INFO) << "CreateInputSurface -- input surface obtained from " "service \"" << client->getServiceName() << "\""; return inputSurface; } } LOG(WARNING) << "CreateInputSurface -- failed to create an input surface " "from all services"; return nullptr; } bool Codec2Client::IsAidlSelected() { return c2_aidl::utils::IsSelected(); } // Codec2Client::Interface Codec2Client::Interface::Interface(const sp& base) : Configurable{ [base]() -> sp { Return> transResult = base->getConfigurable(); return transResult.isOk() ? static_cast>(transResult) : nullptr; }() }, mHidlBase{base} { } Codec2Client::Interface::Interface(const std::shared_ptr& base) : Configurable{ [base]() -> std::shared_ptr { std::shared_ptr aidlConfigurable; ::ndk::ScopedAStatus transStatus = base->getConfigurable(&aidlConfigurable); return transStatus.isOk() ? aidlConfigurable : nullptr; }() }, mAidlBase{base} { } // Codec2Client::Component class Codec2Client::Component::AidlDeathManager { public: AidlDeathManager() : mSeq(0), mDeathRecipient(AIBinder_DeathRecipient_new(OnBinderDied)) { } ~AidlDeathManager() = default; bool linkToDeath( const std::shared_ptr &comp, const std::shared_ptr &listener, size_t *seqPtr) { std::unique_lock lock(mMutex); size_t seq = mSeq++; if (!mMap.try_emplace(seq, comp, listener).second) { return false; } if (STATUS_OK != AIBinder_linkToDeath( comp->mAidlBase->asBinder().get(), mDeathRecipient.get(), (void *)seq)) { mMap.erase(seq); return false; } *seqPtr = seq; return true; } void unlinkToDeath(size_t seq, const std::shared_ptr &base) { std::unique_lock lock(mMutex); AIBinder_unlinkToDeath(base->asBinder().get(), mDeathRecipient.get(), (void *)seq); mMap.erase(seq); } private: std::mutex mMutex; size_t mSeq; typedef std::tuple, std::weak_ptr> Context; std::map mMap; ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient; bool extractContext(size_t seq, Context *context) { std::unique_lock lock(mMutex); auto node = mMap.extract(seq); if (!node) { return false; } *context = node.mapped(); return true; } static void OnBinderDied(void *cookie) { size_t seq = size_t(cookie); Context context; if (!Component::GetAidlDeathManager()->extractContext(seq, &context)) { return; } std::weak_ptr weakComponent; std::weak_ptr weakListener; std::tie(weakComponent, weakListener) = context; if (std::shared_ptr listener = weakListener.lock()) { listener->onDeath(weakComponent); } else { LOG(DEBUG) << "onDeath -- listener died."; } } }; Codec2Client::Component::Component(const sp& base) : Configurable{ [base]() -> sp { Return> transResult1 = base->getInterface(); if (!transResult1.isOk()) { return nullptr; } Return> transResult2 = static_cast>(transResult1)-> getConfigurable(); return transResult2.isOk() ? static_cast>(transResult2) : nullptr; }() }, mHidlBase1_0{base}, mHidlBase1_1{HidlBase1_1::castFrom(base)}, mHidlBase1_2{HidlBase1_2::castFrom(base)}, mHidlBufferPoolSender{std::make_unique()}, mOutputBufferQueue{std::make_unique()} { } Codec2Client::Component::Component(const sp& base) : Configurable{ [base]() -> sp { Return> transResult1 = base->getInterface(); if (!transResult1.isOk()) { return nullptr; } Return> transResult2 = static_cast>(transResult1)-> getConfigurable(); return transResult2.isOk() ? static_cast>(transResult2) : nullptr; }() }, mHidlBase1_0{base}, mHidlBase1_1{base}, mHidlBase1_2{HidlBase1_2::castFrom(base)}, mHidlBufferPoolSender{std::make_unique()}, mOutputBufferQueue{std::make_unique()} { } Codec2Client::Component::Component(const sp& base) : Configurable{ [base]() -> sp { Return> transResult1 = base->getInterface(); if (!transResult1.isOk()) { return nullptr; } Return> transResult2 = static_cast>(transResult1)-> getConfigurable(); return transResult2.isOk() ? static_cast>(transResult2) : nullptr; }() }, mHidlBase1_0{base}, mHidlBase1_1{base}, mHidlBase1_2{base}, mHidlBufferPoolSender{std::make_unique()}, mOutputBufferQueue{std::make_unique()} { } Codec2Client::Component::Component(const std::shared_ptr &base) : Configurable{ [base]() -> std::shared_ptr { std::shared_ptr aidlIntf; ::ndk::ScopedAStatus transStatus = base->getInterface(&aidlIntf); if (!transStatus.isOk()) { return nullptr; } std::shared_ptr aidlConfigurable; transStatus = aidlIntf->getConfigurable(&aidlConfigurable); return transStatus.isOk() ? aidlConfigurable : nullptr; }() }, mAidlBase{base}, mAidlBufferPoolSender{std::make_unique()}, mGraphicBufferAllocators{std::make_unique()} { } Codec2Client::Component::~Component() { if (mAidlDeathSeq) { GetAidlDeathManager()->unlinkToDeath(*mAidlDeathSeq, mAidlBase); } } c2_status_t Codec2Client::Component::createBlockPool( C2Allocator::id_t id, C2BlockPool::local_id_t* blockPoolId, std::shared_ptr* configurable) { if (mAidlBase) { c2_aidl::IComponent::BlockPool aidlBlockPool; c2_status_t status = C2_OK; // TODO: Temporary mapping for the current CCodecBufferChannel. // Handle this properly and remove this temporary allocator mapping. id = id == C2PlatformAllocatorStore::BUFFERQUEUE ? C2PlatformAllocatorStore::IGBA : id; c2_aidl::IComponent::BlockPoolAllocator allocator; allocator.allocatorId = id; if (id == C2PlatformAllocatorStore::IGBA) { std::shared_ptr gba = mGraphicBufferAllocators->create(); ::ndk::ScopedFileDescriptor waitableFd; ::ndk::ScopedAStatus ret = gba->getWaitableFd(&waitableFd); status = GetC2Status(ret, "Gba::getWaitableFd"); if (status != C2_OK) { return status; } c2_aidl::IComponent::GbAllocator gbAllocator; gbAllocator.waitableFd = std::move(waitableFd); gbAllocator.igba = c2_aidl::IGraphicBufferAllocator::fromBinder(gba->asBinder()); allocator.gbAllocator = std::move(gbAllocator); ::ndk::ScopedAStatus transStatus = mAidlBase->createBlockPool( allocator, &aidlBlockPool); status = GetC2Status(transStatus, "createBlockPool"); if (status != C2_OK) { return status; } mGraphicBufferAllocators->setCurrentId(aidlBlockPool.blockPoolId); } else { ::ndk::ScopedAStatus transStatus = mAidlBase->createBlockPool( allocator, &aidlBlockPool); status = GetC2Status(transStatus, "createBlockPool"); if (status != C2_OK) { return status; } } *blockPoolId = aidlBlockPool.blockPoolId; *configurable = std::make_shared(aidlBlockPool.configurable); return C2_OK; } c2_status_t status; Return transStatus = mHidlBase1_0->createBlockPool( static_cast(id), [&status, blockPoolId, configurable]( c2_hidl::Status s, uint64_t pId, const sp& c) { status = static_cast(s); configurable->reset(); if (status != C2_OK) { LOG(DEBUG) << "createBlockPool -- call failed: " << status << "."; return; } *blockPoolId = static_cast(pId); *configurable = std::make_shared(c); }); if (!transStatus.isOk()) { LOG(ERROR) << "createBlockPool -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; } c2_status_t Codec2Client::Component::destroyBlockPool( C2BlockPool::local_id_t localId) { if (mAidlBase) { mGraphicBufferAllocators->remove(localId); ::ndk::ScopedAStatus transStatus = mAidlBase->destroyBlockPool(localId); return GetC2Status(transStatus, "destroyBlockPool"); } Return transResult = mHidlBase1_0->destroyBlockPool( static_cast(localId)); if (!transResult.isOk()) { LOG(ERROR) << "destroyBlockPool -- transaction failed."; return C2_TRANSACTION_FAILED; } return static_cast(static_cast(transResult)); } void Codec2Client::Component::handleOnWorkDone( const std::list> &workItems) { if (mAidlBase) { holdIgbaBlocks(workItems); } else { // Output bufferqueue-based blocks' lifetime management mOutputBufferQueue->holdBufferQueueBlocks(workItems); } } c2_status_t Codec2Client::Component::queue( std::list>* const items) { if (mAidlBase) { c2_aidl::WorkBundle workBundle; if (!c2_aidl::utils::ToAidl(&workBundle, *items, mAidlBufferPoolSender.get())) { LOG(ERROR) << "queue -- bad input."; return C2_TRANSACTION_FAILED; } ::ndk::ScopedAStatus transStatus = mAidlBase->queue(workBundle); return GetC2Status(transStatus, "queue"); } c2_hidl::WorkBundle workBundle; if (!c2_hidl::utils::objcpy(&workBundle, *items, mHidlBufferPoolSender.get())) { LOG(ERROR) << "queue -- bad input."; return C2_TRANSACTION_FAILED; } Return transStatus = mHidlBase1_0->queue(workBundle); if (!transStatus.isOk()) { LOG(ERROR) << "queue -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "queue -- call failed: " << status << "."; } return status; } c2_status_t Codec2Client::Component::flush( C2Component::flush_mode_t mode, std::list>* const flushedWork) { (void)mode; // Flush mode isn't supported in HIDL/AIDL yet. c2_status_t status = C2_OK; if (mAidlBase) { c2_aidl::WorkBundle workBundle; ::ndk::ScopedAStatus transStatus = mAidlBase->flush(&workBundle); c2_status_t status = GetC2Status(transStatus, "flush"); if (status != C2_OK) { return status; } if (!c2_aidl::utils::FromAidl(flushedWork, workBundle)) { LOG(DEBUG) << "flush -- flushedWork corrupted."; return C2_CORRUPTED; } } else { Return transStatus = mHidlBase1_0->flush( [&status, flushedWork]( c2_hidl::Status s, const c2_hidl::WorkBundle& wb) { status = static_cast(s); if (status != C2_OK) { LOG(DEBUG) << "flush -- call failed: " << status << "."; return; } if (!c2_hidl::utils::objcpy(flushedWork, wb)) { status = C2_CORRUPTED; } else { status = C2_OK; } }); if (!transStatus.isOk()) { LOG(ERROR) << "flush -- transaction failed."; return C2_TRANSACTION_FAILED; } } // Indices of flushed work items. std::vector flushedIndices; for (const std::unique_ptr &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()); } } } if (mAidlBase) { holdIgbaBlocks(*flushedWork); } else { // Output bufferqueue-based blocks' lifetime management mOutputBufferQueue->holdBufferQueueBlocks(*flushedWork); } return status; } c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) { if (mAidlBase) { ::ndk::ScopedAStatus transStatus = mAidlBase->drain( mode == C2Component::DRAIN_COMPONENT_WITH_EOS); return GetC2Status(transStatus, "drain"); } Return transStatus = mHidlBase1_0->drain( mode == C2Component::DRAIN_COMPONENT_WITH_EOS); if (!transStatus.isOk()) { LOG(ERROR) << "drain -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "drain -- call failed: " << status << "."; } return status; } c2_status_t Codec2Client::Component::start() { if (mAidlBase) { ::ndk::ScopedAStatus transStatus = mAidlBase->start(); return GetC2Status(transStatus, "start"); } Return transStatus = mHidlBase1_0->start(); if (!transStatus.isOk()) { LOG(ERROR) << "start -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "start -- call failed: " << status << "."; } return status; } c2_status_t Codec2Client::Component::stop() { if (mAidlBase) { ::ndk::ScopedAStatus transStatus = mAidlBase->stop(); return GetC2Status(transStatus, "stop"); } Return transStatus = mHidlBase1_0->stop(); if (!transStatus.isOk()) { LOG(ERROR) << "stop -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "stop -- call failed: " << status << "."; } return status; } c2_status_t Codec2Client::Component::reset() { if (mAidlBase) { ::ndk::ScopedAStatus transStatus = mAidlBase->reset(); return GetC2Status(transStatus, "reset"); } Return transStatus = mHidlBase1_0->reset(); if (!transStatus.isOk()) { LOG(ERROR) << "reset -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "reset -- call failed: " << status << "."; } return status; } c2_status_t Codec2Client::Component::release() { if (mAidlBase) { ::ndk::ScopedAStatus transStatus = mAidlBase->release(); return GetC2Status(transStatus, "release"); } Return transStatus = mHidlBase1_0->release(); if (!transStatus.isOk()) { LOG(ERROR) << "release -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "release -- call failed: " << status << "."; } return status; } c2_status_t Codec2Client::Component::configureVideoTunnel( uint32_t avSyncHwId, native_handle_t** sidebandHandle) { *sidebandHandle = nullptr; if (mAidlBase) { ::aidl::android::hardware::common::NativeHandle handle; ::ndk::ScopedAStatus transStatus = mAidlBase->configureVideoTunnel(avSyncHwId, &handle); c2_status_t status = GetC2Status(transStatus, "configureVideoTunnel"); if (status != C2_OK) { return status; } if (isAidlNativeHandleEmpty(handle)) { LOG(DEBUG) << "configureVideoTunnel -- empty handle returned"; } else { *sidebandHandle = dupFromAidl(handle); } return C2_OK; } if (!mHidlBase1_1) { return C2_OMITTED; } c2_status_t status{}; Return transStatus = mHidlBase1_1->configureVideoTunnel(avSyncHwId, [&status, sidebandHandle]( c2_hidl::Status s, hardware::hidl_handle const& h) { status = static_cast(s); if (h.getNativeHandle()) { *sidebandHandle = native_handle_clone(h.getNativeHandle()); } }); if (!transStatus.isOk()) { LOG(ERROR) << "configureVideoTunnel -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; } c2_status_t Codec2Client::Component::setOutputSurface( C2BlockPool::local_id_t blockPoolId, const sp& surface, uint32_t generation, int maxDequeueCount) { if (mAidlBase) { std::shared_ptr gba = mGraphicBufferAllocators->current(); if (!gba) { LOG(ERROR) << "setOutputSurface for AIDL -- " "GraphicBufferAllocator was not created."; return C2_CORRUPTED; } bool ret = gba->configure(surface, generation, maxDequeueCount); return ret ? C2_OK : C2_CORRUPTED; } uint64_t bqId = 0; sp nullIgbp; sp nullHgbp; sp igbp = surface ? surface->getHalInterface() : nullHgbp; if (surface && !igbp) { igbp = new B2HGraphicBufferProducer2(surface); } std::scoped_lock lock(mOutputMutex); std::shared_ptr syncObj; if (!surface) { mOutputBufferQueue->configure(nullIgbp, generation, 0, maxDequeueCount, nullptr); } else if (surface->getUniqueId(&bqId) != OK) { LOG(ERROR) << "setOutputSurface -- " "cannot obtain bufferqueue id."; bqId = 0; mOutputBufferQueue->configure(nullIgbp, generation, 0, maxDequeueCount, nullptr); } else { mOutputBufferQueue->configure(surface, generation, bqId, maxDequeueCount, mHidlBase1_2 ? &syncObj : nullptr); } // set consumer bits // TODO: should this get incorporated into setOutputSurface method so that consumer bits // can be set atomically? uint64_t consumerUsage = kDefaultConsumerUsage; { if (surface) { uint64_t usage = 0; status_t err = surface->getConsumerUsage(&usage); if (err != NO_ERROR) { ALOGD("setOutputSurface -- failed to get consumer usage bits (%d/%s). ignoring", err, asString(err)); } else { // Note: we are adding the default usage because components must support // producing output frames that can be displayed an all output surfaces. // TODO: do not set usage for tunneled scenario. It is unclear if consumer usage // is meaningful in a tunneled scenario; on one hand output buffers exist, but // they do not exist inside of C2 scope. Any buffer usage shall be communicated // through the sideband channel. consumerUsage = usage | kDefaultConsumerUsage; } } C2StreamUsageTuning::output outputUsage{ 0u, C2AndroidMemoryUsage::FromGrallocUsage(consumerUsage).expected}; std::vector> failures; c2_status_t err = config({&outputUsage}, C2_MAY_BLOCK, &failures); if (err != C2_OK) { ALOGD("setOutputSurface -- failed to set consumer usage (%d/%s)", err, asString(err)); } } ALOGD("setOutputSurface -- generation=%u consumer usage=%#llx%s", generation, (long long)consumerUsage, syncObj ? " sync" : ""); Return transStatus = syncObj ? mHidlBase1_2->setOutputSurfaceWithSyncObj( static_cast(blockPoolId), bqId == 0 ? nullHgbp : igbp, *syncObj) : mHidlBase1_0->setOutputSurface( static_cast(blockPoolId), bqId == 0 ? nullHgbp : igbp); mOutputBufferQueue->expireOldWaiters(); if (!transStatus.isOk()) { LOG(ERROR) << "setOutputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "setOutputSurface -- call failed: " << status << "."; } ALOGD("Surface configure completed"); return status; } status_t Codec2Client::Component::queueToOutputSurface( const C2ConstGraphicBlock& block, const QueueBufferInput& input, QueueBufferOutput* output) { ScopedTrace trace(ATRACE_TAG,"Codec2Client::Component::queueToOutputSurface"); if (mAidlBase) { std::shared_ptr gba = mGraphicBufferAllocators->current(); if (gba) { return gba->displayBuffer(block, input, output); } else { return C2_NOT_FOUND; } } return mOutputBufferQueue->outputBuffer(block, input, output); } void Codec2Client::Component::pollForRenderedFrames(FrameEventHistoryDelta* delta) { if (mAidlBase) { // TODO b/311348680 return; } mOutputBufferQueue->pollForRenderedFrames(delta); } void Codec2Client::Component::setOutputSurfaceMaxDequeueCount( int maxDequeueCount) { if (mAidlBase) { std::shared_ptr gba = mGraphicBufferAllocators->current(); if (gba) { gba->updateMaxDequeueBufferCount(maxDequeueCount); } return; } mOutputBufferQueue->updateMaxDequeueBufferCount(maxDequeueCount); } void Codec2Client::Component::stopUsingOutputSurface( C2BlockPool::local_id_t blockPoolId) { if (mAidlBase) { std::shared_ptr gba = mGraphicBufferAllocators->current(); if (gba) { gba->reset(); } return; } std::scoped_lock lock(mOutputMutex); mOutputBufferQueue->stop(); Return transStatus = mHidlBase1_0->setOutputSurface( static_cast(blockPoolId), nullptr); if (!transStatus.isOk()) { LOG(ERROR) << "setOutputSurface(stopUsingOutputSurface) -- transaction failed."; } else { c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "setOutputSurface(stopUsingOutputSurface) -- call failed: " << status << "."; } } mOutputBufferQueue->expireOldWaiters(); } void Codec2Client::Component::onBufferReleasedFromOutputSurface( uint32_t generation) { if (mAidlBase) { std::shared_ptr gba = mGraphicBufferAllocators->current(); if (gba) { gba->onBufferReleased(generation); } return; } mOutputBufferQueue->onBufferReleased(generation); } void Codec2Client::Component::holdIgbaBlocks( const std::list>& workList) { if (!mAidlBase) { return; } std::shared_ptr gba = mGraphicBufferAllocators->current(); if (!gba) { return; } std::shared_ptr igba = c2_aidl::IGraphicBufferAllocator::fromBinder(gba->asBinder()); for (const std::unique_ptr& work : workList) { if (!work) [[unlikely]] { continue; } for (const std::unique_ptr& worklet : work->worklets) { if (!worklet) { continue; } for (const std::shared_ptr& buffer : worklet->output.buffers) { if (buffer) { for (const C2ConstGraphicBlock& block : buffer->data().graphicBlocks()) { std::shared_ptr<_C2BlockPoolData> poolData = _C2BlockFactory::GetGraphicBlockPoolData(block); _C2BlockFactory::RegisterIgba(poolData, igba); } } } } } } c2_status_t Codec2Client::Component::connectToInputSurface( const std::shared_ptr& inputSurface, std::shared_ptr* connection) { if (mAidlBase) { // FIXME return C2_OMITTED; } c2_status_t status; Return transStatus = mHidlBase1_0->connectToInputSurface( inputSurface->mBase, [&status, connection]( c2_hidl::Status s, const sp& c) { status = static_cast(s); if (status != C2_OK) { LOG(DEBUG) << "connectToInputSurface -- call failed: " << status << "."; return; } *connection = std::make_shared(c); }); if (!transStatus.isOk()) { LOG(ERROR) << "connectToInputSurface -- transaction failed"; return C2_TRANSACTION_FAILED; } return status; } c2_status_t Codec2Client::Component::connectToOmxInputSurface( const sp& producer, const sp& source, std::shared_ptr* connection) { if (mAidlBase) { LOG(WARNING) << "Connecting to OMX input surface is not supported for AIDL C2 HAL"; return C2_OMITTED; } c2_status_t status; Return transStatus = mHidlBase1_0->connectToOmxInputSurface( producer, source, [&status, connection]( c2_hidl::Status s, const sp& c) { status = static_cast(s); if (status != C2_OK) { LOG(DEBUG) << "connectToOmxInputSurface -- call failed: " << status << "."; return; } *connection = std::make_shared(c); }); if (!transStatus.isOk()) { LOG(ERROR) << "connectToOmxInputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; } c2_status_t Codec2Client::Component::disconnectFromInputSurface() { if (mAidlBase) { // FIXME return C2_OMITTED; } Return transStatus = mHidlBase1_0->disconnectFromInputSurface(); if (!transStatus.isOk()) { LOG(ERROR) << "disconnectToInputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "disconnectFromInputSurface -- call failed: " << status << "."; } return status; } Codec2Client::Component::AidlDeathManager *Codec2Client::Component::GetAidlDeathManager() { // This object never gets destructed static AidlDeathManager *sManager = new AidlDeathManager(); return sManager; } c2_status_t Codec2Client::Component::setDeathListener( const std::shared_ptr& component, const std::shared_ptr& listener) { struct HidlDeathRecipient : public hardware::hidl_death_recipient { std::weak_ptr component; std::weak_ptr base; virtual void serviceDied( uint64_t /* cookie */, const wp<::android::hidl::base::V1_0::IBase>& /* who */ ) override { if (std::shared_ptr listener = base.lock()) { listener->onDeath(component); } else { LOG(DEBUG) << "onDeath -- listener died."; } } }; if (component->mAidlBase) { size_t seq; if (GetAidlDeathManager()->linkToDeath(component, listener, &seq)) { component->mAidlDeathSeq = seq; } return C2_OK; } sp deathRecipient = new HidlDeathRecipient(); deathRecipient->base = listener; deathRecipient->component = component; component->mDeathRecipient = deathRecipient; Return transResult = component->mHidlBase1_0->linkToDeath( component->mDeathRecipient, 0); if (!transResult.isOk()) { LOG(ERROR) << "setDeathListener -- linkToDeath() transaction failed."; return C2_TRANSACTION_FAILED; } if (!static_cast(transResult)) { LOG(DEBUG) << "setDeathListener -- linkToDeath() call failed."; return C2_CORRUPTED; } return C2_OK; } // Codec2Client::InputSurface Codec2Client::InputSurface::InputSurface(const sp& base) : Configurable{ [base]() -> sp { Return> transResult = base->getConfigurable(); return transResult.isOk() ? static_cast>(transResult) : nullptr; }() }, mBase{base}, mGraphicBufferProducer{new H2BGraphicBufferProducer2([base]() -> sp { Return> transResult = base->getGraphicBufferProducer(); return transResult.isOk() ? static_cast>(transResult) : nullptr; }())} { } sp Codec2Client::InputSurface::getGraphicBufferProducer() const { return mGraphicBufferProducer; } sp Codec2Client::InputSurface::getHalInterface() const { return mBase; } // Codec2Client::InputSurfaceConnection Codec2Client::InputSurfaceConnection::InputSurfaceConnection( const sp& base) : Configurable{ [base]() -> sp { Return> transResult = base->getConfigurable(); return transResult.isOk() ? static_cast>(transResult) : nullptr; }() }, mBase{base} { } c2_status_t Codec2Client::InputSurfaceConnection::disconnect() { Return transResult = mBase->disconnect(); return static_cast(static_cast(transResult)); } } // namespace android