| /* Copyright (C) 2020 The Android Open Source Project |
| ** |
| ** This software is licensed under the terms of the GNU General Public |
| ** License version 2, as published by the Free Software Foundation, and |
| ** may be copied, distributed, and modified under those terms. |
| ** |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| */ |
| |
| #define LOG_TAG "GoldfishComponentStore" |
| |
| #include <goldfish_codec2/store/GoldfishComponentStore.h> |
| |
| #include <dlfcn.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <mutex> |
| |
| #include <C2.h> |
| #include <C2Config.h> |
| #include <cutils/properties.h> |
| #include <log/log.h> |
| |
| namespace android { |
| |
| // static |
| std::shared_ptr<C2ComponentStore> GoldfishComponentStore::Create() { |
| ALOGI("%s()", __func__); |
| |
| static std::mutex mutex; |
| static std::weak_ptr<C2ComponentStore> platformStore; |
| |
| std::lock_guard<std::mutex> lock(mutex); |
| std::shared_ptr<C2ComponentStore> store = platformStore.lock(); |
| if (store != nullptr) |
| return store; |
| |
| store = std::shared_ptr<C2ComponentStore>(new GoldfishComponentStore()); |
| platformStore = store; |
| return store; |
| } |
| |
| C2String GoldfishComponentStore::getName() const { |
| return "android.componentStore.goldfish"; |
| } |
| |
| c2_status_t GoldfishComponentStore::ComponentModule::init(std::string libPath) { |
| ALOGI("in %s", __func__); |
| ALOGI("loading dll of path %s", libPath.c_str()); |
| mLibHandle = dlopen(libPath.c_str(), RTLD_NOW | RTLD_NODELETE); |
| LOG_ALWAYS_FATAL_IF(mLibHandle == nullptr, "could not dlopen %s: %s", |
| libPath.c_str(), dlerror()); |
| |
| createFactory = (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym( |
| mLibHandle, "CreateCodec2Factory"); |
| LOG_ALWAYS_FATAL_IF(createFactory == nullptr, "createFactory is null in %s", |
| libPath.c_str()); |
| |
| destroyFactory = (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym( |
| mLibHandle, "DestroyCodec2Factory"); |
| LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr, |
| "destroyFactory is null in %s", libPath.c_str()); |
| |
| mComponentFactory = createFactory(); |
| if (mComponentFactory == nullptr) { |
| ALOGD("could not create factory in %s", libPath.c_str()); |
| mInit = C2_NO_MEMORY; |
| } else { |
| mInit = C2_OK; |
| } |
| |
| if (mInit != C2_OK) { |
| return mInit; |
| } |
| |
| std::shared_ptr<C2ComponentInterface> intf; |
| c2_status_t res = createInterface(0, &intf); |
| if (res != C2_OK) { |
| ALOGD("failed to create interface: %d", res); |
| return mInit; |
| } |
| |
| std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) |
| C2Component::Traits); |
| if (traits) { |
| traits->name = intf->getName(); |
| |
| C2ComponentKindSetting kind; |
| C2ComponentDomainSetting domain; |
| res = intf->query_vb({&kind, &domain}, {}, C2_MAY_BLOCK, nullptr); |
| bool fixDomain = res != C2_OK; |
| if (res == C2_OK) { |
| traits->kind = kind.value; |
| traits->domain = domain.value; |
| } else { |
| // TODO: remove this fall-back |
| ALOGD("failed to query interface for kind and domain: %d", res); |
| |
| traits->kind = (traits->name.find("encoder") != std::string::npos) |
| ? C2Component::KIND_ENCODER |
| : (traits->name.find("decoder") != std::string::npos) |
| ? C2Component::KIND_DECODER |
| : C2Component::KIND_OTHER; |
| } |
| |
| uint32_t mediaTypeIndex = |
| traits->kind == C2Component::KIND_ENCODER |
| ? C2PortMediaTypeSetting::output::PARAM_TYPE |
| : C2PortMediaTypeSetting::input::PARAM_TYPE; |
| std::vector<std::unique_ptr<C2Param>> params; |
| res = intf->query_vb({}, {mediaTypeIndex}, C2_MAY_BLOCK, ¶ms); |
| if (res != C2_OK) { |
| ALOGD("failed to query interface: %d", res); |
| return mInit; |
| } |
| if (params.size() != 1u) { |
| ALOGD("failed to query interface: unexpected vector size: %zu", |
| params.size()); |
| return mInit; |
| } |
| C2PortMediaTypeSetting *mediaTypeConfig = |
| C2PortMediaTypeSetting::From(params[0].get()); |
| if (mediaTypeConfig == nullptr) { |
| ALOGD("failed to query media type"); |
| return mInit; |
| } |
| traits->mediaType = std::string( |
| mediaTypeConfig->m.value, |
| strnlen(mediaTypeConfig->m.value, mediaTypeConfig->flexCount())); |
| |
| if (fixDomain) { |
| if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) { |
| traits->domain = C2Component::DOMAIN_AUDIO; |
| } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) { |
| traits->domain = C2Component::DOMAIN_VIDEO; |
| } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) { |
| traits->domain = C2Component::DOMAIN_IMAGE; |
| } else { |
| traits->domain = C2Component::DOMAIN_OTHER; |
| } |
| } |
| |
| // TODO: get this properly from the store during emplace |
| switch (traits->domain) { |
| case C2Component::DOMAIN_AUDIO: |
| traits->rank = 8; |
| break; |
| default: |
| traits->rank = 512; |
| } |
| |
| params.clear(); |
| res = intf->query_vb({}, {C2ComponentAliasesSetting::PARAM_TYPE}, |
| C2_MAY_BLOCK, ¶ms); |
| if (res == C2_OK && params.size() == 1u) { |
| C2ComponentAliasesSetting *aliasesSetting = |
| C2ComponentAliasesSetting::From(params[0].get()); |
| if (aliasesSetting) { |
| // Split aliases on ',' |
| // This looks simpler in plain C and even std::string would |
| // still make a copy. |
| char *aliases = ::strndup(aliasesSetting->m.value, |
| aliasesSetting->flexCount()); |
| ALOGD("'%s' has aliases: '%s'", intf->getName().c_str(), |
| aliases); |
| |
| for (char *tok, *ptr, *str = aliases; |
| (tok = ::strtok_r(str, ",", &ptr)); str = nullptr) { |
| traits->aliases.push_back(tok); |
| ALOGD("adding alias: '%s'", tok); |
| } |
| free(aliases); |
| } |
| } |
| } |
| mTraits = traits; |
| |
| return mInit; |
| } |
| |
| GoldfishComponentStore::ComponentModule::~ComponentModule() { |
| ALOGI("in %s", __func__); |
| if (destroyFactory && mComponentFactory) { |
| destroyFactory(mComponentFactory); |
| } |
| if (mLibHandle) { |
| ALOGI("unloading dll"); |
| dlclose(mLibHandle); |
| } |
| } |
| |
| c2_status_t GoldfishComponentStore::ComponentModule::createInterface( |
| c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface, |
| std::function<void(::C2ComponentInterface *)> deleter) { |
| interface->reset(); |
| if (mInit != C2_OK) { |
| return mInit; |
| } |
| std::shared_ptr<ComponentModule> module = shared_from_this(); |
| c2_status_t res = mComponentFactory->createInterface( |
| id, interface, [module, deleter](C2ComponentInterface *p) mutable { |
| // capture module so that we ensure we still have it while deleting |
| // interface |
| deleter(p); // delete interface first |
| module.reset(); // remove module ref (not technically needed) |
| }); |
| ALOGI("created interface"); |
| return res; |
| } |
| |
| c2_status_t GoldfishComponentStore::ComponentModule::createComponent( |
| c2_node_id_t id, std::shared_ptr<C2Component> *component, |
| std::function<void(::C2Component *)> deleter) { |
| component->reset(); |
| if (mInit != C2_OK) { |
| return mInit; |
| } |
| std::shared_ptr<ComponentModule> module = shared_from_this(); |
| c2_status_t res = mComponentFactory->createComponent( |
| id, component, [module, deleter](C2Component *p) mutable { |
| // capture module so that we ensure we still have it while deleting |
| // component |
| deleter(p); // delete component first |
| module.reset(); // remove module ref (not technically needed) |
| }); |
| ALOGI("created component"); |
| return res; |
| } |
| |
| std::shared_ptr<const C2Component::Traits> |
| GoldfishComponentStore::ComponentModule::getTraits() { |
| std::unique_lock<std::recursive_mutex> lock(mLock); |
| return mTraits; |
| } |
| |
| // We have a property set indicating whether to use the host side codec |
| // or not (ro.boot.qemu.hwcodec.<mLibNameSuffix>). |
| static std::string BuildHWCodecPropName(const char *libname) { |
| using namespace std::literals::string_literals; |
| return "ro.boot.qemu.hwcodec."s + libname; |
| } |
| |
| static bool useAndroidGoldfishComponentInstance(const char *libname) { |
| const std::string propName = BuildHWCodecPropName(libname); |
| char propValue[PROP_VALUE_MAX]; |
| bool myret = property_get(propName.c_str(), propValue, "") > 0 && |
| strcmp("2", propValue) == 0; |
| if (myret) { |
| ALOGD("%s %d found prop %s val %s", __func__, __LINE__, propName.c_str(), |
| propValue); |
| } |
| return myret; |
| } |
| |
| GoldfishComponentStore::GoldfishComponentStore() |
| : mVisited(false), mReflector(std::make_shared<C2ReflectorHelper>()) { |
| |
| ALOGW("created goldfish store %p reflector of param %p", this, |
| mReflector.get()); |
| auto emplace = [this](const char *libPath) { |
| mComponents.emplace(libPath, libPath); |
| }; |
| |
| if (useAndroidGoldfishComponentInstance("vpxdec")) { |
| emplace("libcodec2_goldfish_vp8dec.so"); |
| emplace("libcodec2_goldfish_vp9dec.so"); |
| } |
| if (useAndroidGoldfishComponentInstance("avcdec")) { |
| emplace("libcodec2_goldfish_avcdec.so"); |
| } |
| } |
| |
| c2_status_t |
| GoldfishComponentStore::copyBuffer(std::shared_ptr<C2GraphicBuffer> src, |
| std::shared_ptr<C2GraphicBuffer> dst) { |
| (void)src; |
| (void)dst; |
| return C2_OMITTED; |
| } |
| |
| c2_status_t GoldfishComponentStore::query_sm( |
| const std::vector<C2Param *> &stackParams, |
| const std::vector<C2Param::Index> &heapParamIndices, |
| std::vector<std::unique_ptr<C2Param>> *const heapParams) const { |
| (void)heapParams; |
| return stackParams.empty() && heapParamIndices.empty() ? C2_OK |
| : C2_BAD_INDEX; |
| } |
| |
| c2_status_t GoldfishComponentStore::config_sm( |
| const std::vector<C2Param *> ¶ms, |
| std::vector<std::unique_ptr<C2SettingResult>> *const failures) { |
| (void)failures; |
| return params.empty() ? C2_OK : C2_BAD_INDEX; |
| } |
| |
| void GoldfishComponentStore::visitComponents() { |
| std::lock_guard<std::mutex> lock(mMutex); |
| if (mVisited) { |
| return; |
| } |
| for (auto &pathAndLoader : mComponents) { |
| const C2String &path = pathAndLoader.first; |
| ComponentLoader &loader = pathAndLoader.second; |
| std::shared_ptr<ComponentModule> module; |
| if (loader.fetchModule(&module) == C2_OK) { |
| std::shared_ptr<const C2Component::Traits> traits = |
| module->getTraits(); |
| if (traits) { |
| mComponentList.push_back(traits); |
| mComponentNameToPath.emplace(traits->name, path); |
| for (const C2String &alias : traits->aliases) { |
| mComponentNameToPath.emplace(alias, path); |
| } |
| } |
| } |
| } |
| mVisited = true; |
| } |
| |
| std::vector<std::shared_ptr<const C2Component::Traits>> |
| GoldfishComponentStore::listComponents() { |
| // This method SHALL return within 500ms. |
| visitComponents(); |
| return mComponentList; |
| } |
| |
| c2_status_t GoldfishComponentStore::findComponent( |
| C2String name, std::shared_ptr<ComponentModule> *module) { |
| (*module).reset(); |
| visitComponents(); |
| |
| auto pos = mComponentNameToPath.find(name); |
| if (pos != mComponentNameToPath.end()) { |
| return mComponents.at(pos->second).fetchModule(module); |
| } |
| return C2_NOT_FOUND; |
| } |
| |
| c2_status_t GoldfishComponentStore::createComponent( |
| C2String name, std::shared_ptr<C2Component> *const component) { |
| // This method SHALL return within 100ms. |
| component->reset(); |
| std::shared_ptr<ComponentModule> module; |
| c2_status_t res = findComponent(name, &module); |
| if (res == C2_OK) { |
| // TODO: get a unique node ID |
| res = module->createComponent(0, component); |
| } |
| return res; |
| } |
| |
| c2_status_t GoldfishComponentStore::createInterface( |
| C2String name, std::shared_ptr<C2ComponentInterface> *const interface) { |
| // This method SHALL return within 100ms. |
| interface->reset(); |
| std::shared_ptr<ComponentModule> module; |
| c2_status_t res = findComponent(name, &module); |
| if (res == C2_OK) { |
| // TODO: get a unique node ID |
| res = module->createInterface(0, interface); |
| } |
| return res; |
| } |
| |
| c2_status_t GoldfishComponentStore::querySupportedParams_nb( |
| std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const { |
| (void)params; |
| return C2_OK; |
| } |
| |
| c2_status_t GoldfishComponentStore::querySupportedValues_sm( |
| std::vector<C2FieldSupportedValuesQuery> &fields) const { |
| return fields.empty() ? C2_OK : C2_BAD_INDEX; |
| } |
| |
| std::shared_ptr<C2ParamReflector> |
| GoldfishComponentStore::getParamReflector() const { |
| return mReflector; |
| } |
| |
| } // namespace android |