/* * 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" #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 // for GRALLOC_USAGE_* #include #include // for NATIVE_WINDOW_QUERY_* #include // for asString(status_t) #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 namespace ::android::hardware::media::c2::V1_1; using namespace ::android::hardware::media::c2::V1_1::utils; using namespace ::android::hardware::media::bufferpool::V2_0; using namespace ::android::hardware::media::bufferpool::V2_0::implementation; 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; 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>(); } }; } // 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 const C2String& Codec2ConfigurableClient::getName() const { return mName; } Codec2ConfigurableClient::Codec2ConfigurableClient( 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::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]( Status s, const 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 (!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::config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector>* const failures) { Params hidlParams; if (!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]( Status s, const hidl_vec f, const 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 SettingResult& sf : f) { if (!objcpy(&(*failures)[i++], sf)) { LOG(ERROR) << "config -- " << "invalid SettingResult returned."; return; } } if (!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::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]( 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 ParamDescriptor& sp : p) { if (!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::querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const { hidl_vec inFields(fields.size()); for (size_t i = 0; i < fields.size(); ++i) { if (!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]( 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 (!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; } // Codec2Client::Component::HidlListener struct Codec2Client::Component::HidlListener : public IComponentListener { std::weak_ptr component; std::weak_ptr base; virtual Return onWorkDone(const WorkBundle& workBundle) override { std::list> workItems; if (!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 (!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(Status s, uint32_t errorCode) override { LOG(DEBUG) << "onError --" << " status = " << s << ", errorCode = " << errorCode << "."; if (std::shared_ptr listener = base.lock()) { listener->onError(component, s == 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::BufferPoolSender struct Codec2Client::Component::BufferPoolSender : hardware::media::c2::V1_1::utils::DefaultBufferPoolSender { BufferPoolSender() : hardware::media::c2::V1_1::utils::DefaultBufferPoolSender() { } }; // Codec2Client::Component::OutputBufferQueue struct Codec2Client::Component::OutputBufferQueue : hardware::media::c2::OutputBufferQueue { OutputBufferQueue() : hardware::media::c2::OutputBufferQueue() { } }; // Codec2Client Codec2Client::Codec2Client(sp const& base, size_t serviceIndex) : Configurable{ [base]() -> sp { Return> transResult = base->getConfigurable(); return transResult.isOk() ? static_cast>(transResult) : nullptr; }() }, mBase1_0{base}, mBase1_1{Base1_1::castFrom(base)}, mBase1_2{Base1_2::castFrom(base)}, mServiceIndex{serviceIndex} { Return> transResult = base->getPoolClientManager(); if (!transResult.isOk()) { LOG(ERROR) << "getPoolClientManager -- transaction failed."; } else { mHostPoolManager = static_cast>(transResult); } } sp const& Codec2Client::getBase() const { return mBase1_0; } sp const& Codec2Client::getBase1_0() const { return mBase1_0; } sp const& Codec2Client::getBase1_1() const { return mBase1_1; } sp const& Codec2Client::getBase1_2() const { return mBase1_2; } 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) { c2_status_t status; sp hidlListener = new Component::HidlListener{}; hidlListener->base = listener; Return transStatus; if (mBase1_2) { transStatus = mBase1_2->createComponent_1_2( name, hidlListener, ClientManager::getInstance(), [&status, component, hidlListener]( Status s, const sp& c) { status = static_cast(s); if (status != C2_OK) { return; } *component = std::make_shared(c); hidlListener->component = *component; }); } else if (mBase1_1) { transStatus = mBase1_1->createComponent_1_1( name, hidlListener, ClientManager::getInstance(), [&status, component, hidlListener]( Status s, const sp& c) { status = static_cast(s); if (status != C2_OK) { return; } *component = std::make_shared(c); hidlListener->component = *component; }); } else if (mBase1_0) { // ver1_0 transStatus = mBase1_0->createComponent( name, hidlListener, ClientManager::getInstance(), [&status, component, hidlListener]( 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)->mBufferPoolSender->setReceiver(mHostPoolManager); return status; } c2_status_t Codec2Client::createInterface( const C2String& name, std::shared_ptr* const interface) { c2_status_t status; Return transStatus = mBase1_0->createInterface( name, [&status, interface]( 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) { c2_status_t status; Return transStatus = mBase1_0->createInputSurface( [&status, inputSurface]( 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(); Return transStatus = mBase1_0->listComponents( [&traits, &serviceName](Status s, const hidl_vec& t) { if (s != 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 (!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 SimpleParamReflector : public C2ParamReflector { virtual std::unique_ptr describe(C2Param::CoreIndex coreIndex) const { hidl_vec indices(1); indices[0] = static_cast(coreIndex.coreIndex()); std::unique_ptr descriptor; Return transStatus = mBase->getStructDescriptors( indices, [&descriptor]( 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 (!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; } SimpleParamReflector(sp base) : mBase(base) { } sp mBase; }; return std::make_shared(mBase1_0); }; std::vector const& Codec2Client::GetServiceNames() { static std::vector sServiceNames{[]() { 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."; // There are three categories of services based on names. std::vector defaultNames; // Prefixed with "default" std::vector vendorNames; // Prefixed with "vendor" std::vector otherNames; // Others Return transResult; transResult = serviceManager->listManifestByInterface( IComponentStore::descriptor, [&defaultNames, &vendorNames, &otherNames]( hidl_vec const& instanceNames) { for (hidl_string const& instanceName : instanceNames) { char const* name = instanceName.c_str(); if (strncmp(name, "default", 7) == 0) { defaultNames.emplace_back(name); } else if (strncmp(name, "vendor", 6) == 0) { vendorNames.emplace_back(name); } else { otherNames.emplace_back(name); } } }); if (transResult.isOk()) { // Sort service names in each category. std::sort(defaultNames.begin(), defaultNames.end()); std::sort(vendorNames.begin(), vendorNames.end()); std::sort(otherNames.begin(), otherNames.end()); // Concatenate the three lists in this order: default, vendor, // other. std::vector& names = defaultNames; names.reserve(names.size() + vendorNames.size() + otherNames.size()); names.insert(names.end(), std::make_move_iterator(vendorNames.begin()), std::make_move_iterator(vendorNames.end())); names.insert(names.end(), std::make_move_iterator(otherNames.begin()), std::make_move_iterator(otherNames.end())); // 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; } LOG(ERROR) << "Could not retrieve the list of service instances of " << IComponentStore::descriptor << ". Retrying..."; } }()}; 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 << "\""; sp baseStore = Base::getService(name); CHECK(baseStore) << "Codec2 service \"" << name << "\"" " inaccessible for unknown reasons."; LOG(VERBOSE) << "Client to Codec2 service \"" << name << "\" created"; return std::make_shared(baseStore, index); } 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; } // Codec2Client::Listener Codec2Client::Listener::~Listener() { } // Codec2Client::Interface Codec2Client::Interface::Interface(const sp& base) : Configurable{ [base]() -> sp { Return> transResult = base->getConfigurable(); return transResult.isOk() ? static_cast>(transResult) : nullptr; }() }, mBase{base} { } // Codec2Client::Component 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; }() }, mBase1_0{base}, mBase1_1{Base1_1::castFrom(base)}, mBase1_2{Base1_2::castFrom(base)}, mBufferPoolSender{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; }() }, mBase1_0{base}, mBase1_1{base}, mBase1_2{Base1_2::castFrom(base)}, mBufferPoolSender{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; }() }, mBase1_0{base}, mBase1_1{base}, mBase1_2{base}, mBufferPoolSender{std::make_unique()}, mOutputBufferQueue{std::make_unique()} { } Codec2Client::Component::~Component() { } c2_status_t Codec2Client::Component::createBlockPool( C2Allocator::id_t id, C2BlockPool::local_id_t* blockPoolId, std::shared_ptr* configurable) { c2_status_t status; Return transStatus = mBase1_0->createBlockPool( static_cast(id), [&status, blockPoolId, configurable]( 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) { Return transResult = mBase1_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) { // Output bufferqueue-based blocks' lifetime management mOutputBufferQueue->holdBufferQueueBlocks(workItems); } c2_status_t Codec2Client::Component::queue( std::list>* const items) { WorkBundle workBundle; if (!objcpy(&workBundle, *items, mBufferPoolSender.get())) { LOG(ERROR) << "queue -- bad input."; return C2_TRANSACTION_FAILED; } Return transStatus = mBase1_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 yet. c2_status_t status; Return transStatus = mBase1_0->flush( [&status, flushedWork]( Status s, const WorkBundle& wb) { status = static_cast(s); if (status != C2_OK) { LOG(DEBUG) << "flush -- call failed: " << status << "."; return; } if (!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()); } } } // Output bufferqueue-based blocks' lifetime management mOutputBufferQueue->holdBufferQueueBlocks(*flushedWork); return status; } c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) { Return transStatus = mBase1_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() { Return transStatus = mBase1_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() { Return transStatus = mBase1_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() { Return transStatus = mBase1_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() { Return transStatus = mBase1_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 (!mBase1_1) { return C2_OMITTED; } c2_status_t status{}; Return transStatus = mBase1_1->configureVideoTunnel(avSyncHwId, [&status, sidebandHandle]( 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) { 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, mBase1_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) { int usage = 0; status_t err = surface->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &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. // do an unsigned conversion as bit-31 may be 1 consumerUsage = (uint32_t)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 ? mBase1_2->setOutputSurfaceWithSyncObj( static_cast(blockPoolId), bqId == 0 ? nullHgbp : igbp, *syncObj) : mBase1_0->setOutputSurface( static_cast(blockPoolId), bqId == 0 ? nullHgbp : igbp); 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) { return mOutputBufferQueue->outputBuffer(block, input, output); } void Codec2Client::Component::setOutputSurfaceMaxDequeueCount( int maxDequeueCount) { mOutputBufferQueue->updateMaxDequeueBufferCount(maxDequeueCount); } void Codec2Client::Component::stopUsingOutputSurface( C2BlockPool::local_id_t blockPoolId) { std::scoped_lock lock(mOutputMutex); mOutputBufferQueue->stop(); Return transStatus = mBase1_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 << "."; } } } c2_status_t Codec2Client::Component::connectToInputSurface( const std::shared_ptr& inputSurface, std::shared_ptr* connection) { c2_status_t status; Return transStatus = mBase1_0->connectToInputSurface( inputSurface->mBase, [&status, connection]( 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) { c2_status_t status; Return transStatus = mBase1_0->connectToOmxInputSurface( producer, source, [&status, connection]( 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() { Return transStatus = mBase1_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; } 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."; } } }; sp deathRecipient = new HidlDeathRecipient(); deathRecipient->base = listener; deathRecipient->component = component; component->mDeathRecipient = deathRecipient; Return transResult = component->mBase1_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