/* * Copyright (C) 2020 Rockchip Electronics Co. LTD * * 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_TAG "C2RKStore" // #define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include "C2RKPlatformSupport.h" #include "mpp_soc.h" namespace android { #define C2_RK_COMPONENT_PATH "libcodec2_rk_component.so" static bool system_secure_supported(void) { static int result = -1; if (result == -1) { struct stat buffer; result = (stat("/dev/dma_heap/secure", &buffer) == 0); } return (result == 1); }; class C2RKComponentStore : public C2ComponentStore { public: virtual std::vector> listComponents() override; virtual std::shared_ptr getParamReflector() const override; virtual C2String getName() const override; virtual c2_status_t querySupportedValues_sm( std::vector &fields) const override; virtual c2_status_t querySupportedParams_nb( std::vector> *const params) const override; virtual c2_status_t query_sm( const std::vector &stackParams, const std::vector &heapParamIndices, std::vector> *const heapParams) const override; virtual c2_status_t createInterface( C2String name, std::shared_ptr *const interface) override; virtual c2_status_t createComponent( C2String name, std::shared_ptr *const component) override; virtual c2_status_t copyBuffer( std::shared_ptr src, std::shared_ptr dst) override; virtual c2_status_t config_sm( const std::vector ¶ms, std::vector> *const failures) override; C2RKComponentStore(); virtual ~C2RKComponentStore() override = default; private: /** * An object encapsulating a loaded component module. * * \todo provide a way to add traits to known components here to avoid loading the .so-s * for listComponents */ struct ComponentModule : public C2ComponentFactory, public std::enable_shared_from_this { virtual c2_status_t createComponent( c2_node_id_t id, std::shared_ptr *component, ComponentDeleter deleter = std::default_delete()) override; virtual c2_status_t createInterface( c2_node_id_t id, std::shared_ptr *interface, InterfaceDeleter deleter = std::default_delete()) override; /** * \returns the traits of the component in this module. */ std::shared_ptr getTraits(); /** * Creates an uninitialized component module. * * \param name[in] component name. * * \note Only used by ComponentLoader. */ ComponentModule() : mInit(C2_NO_INIT), mLibHandle(nullptr), createFactory(nullptr), destroyFactory(nullptr), mComponentFactory(nullptr) { } /** * Initializes a component module with a given library path. Must be called exactly once. * * \note Only used by ComponentLoader. * * \param libPath[in] library path * * \retval C2_OK the component module has been successfully loaded * \retval C2_NO_MEMORY not enough memory to loading the component module * \retval C2_NOT_FOUND could not locate the component module * \retval C2_CORRUPTED the component module could not be loaded (unexpected) * \retval C2_REFUSED permission denied to load the component module (unexpected) * \retval C2_TIMED_OUT could not load the module within the time limit (unexpected) */ c2_status_t init(std::string componentName); virtual ~ComponentModule() override; typedef ::C2ComponentFactory* (*CreateRKCodec2FactoryFunc)(std::string componentName); typedef void (*DestroyRKCodec2FactoryFunc)(::C2ComponentFactory*); protected: std::recursive_mutex mLock; ///< lock protecting mTraits std::shared_ptr mTraits; ///< cached component traits c2_status_t mInit; ///< initialization result void *mLibHandle; ///< loaded library handle CreateRKCodec2FactoryFunc createFactory; ///< loaded create function DestroyRKCodec2FactoryFunc destroyFactory; ///< loaded destroy function C2ComponentFactory *mComponentFactory; ///< loaded/created component factory }; /** * An object encapsulating a loadable component module. * * \todo make this also work for enumerations */ struct ComponentLoader { /** * Load the component module. * * This method simply returns the component module if it is already currently loaded, or * attempts to load it if it is not. * * \param module[out] pointer to the shared pointer where the loaded module shall be stored. * This will be nullptr on error. * * \retval C2_OK the component module has been successfully loaded * \retval C2_NO_MEMORY not enough memory to loading the component module * \retval C2_NOT_FOUND could not locate the component module * \retval C2_CORRUPTED the component module could not be loaded * \retval C2_REFUSED permission denied to load the component module */ c2_status_t fetchModule(std::shared_ptr *module) { c2_status_t res = C2_OK; std::lock_guard lock(mMutex); std::shared_ptr localModule = mModule.lock(); if (localModule == nullptr) { localModule = std::make_shared(); res = localModule->init(mComponentName); if (res == C2_OK) { mModule = localModule; } } *module = localModule; return res; } /** * Creates a component loader for a specific library path (or name). */ ComponentLoader(std::string compoenentName) : mComponentName(compoenentName) {} private: std::mutex mMutex; ///< mutex guarding the module std::weak_ptr mModule; ///< weak reference to the loaded module std::string mComponentName; ///< library path }; struct Interface : public C2InterfaceHelper { std::shared_ptr mIonUsageInfo; std::shared_ptr mDmaBufUsageInfo; Interface(std::shared_ptr reflector) : C2InterfaceHelper(reflector) { setDerivedInstance(this); struct Setter { static C2R setIonUsage(bool /* mayBlock */, C2P &me) { me.set().heapMask = ~0; me.set().allocFlags = 0; me.set().minAlignment = 0; return C2R::Ok(); } static C2R setDmaBufUsage(bool /* mayBlock */, C2P &me) { long long usage = (long long)me.get().m.usage; if ((usage & C2MemoryUsage::READ_PROTECTED) && system_secure_supported()) { strncpy(me.set().m.heapName, "secure", me.v.flexCount()); } else if (C2DmaBufAllocator::system_uncached_supported() && !(usage & (C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE))) { strncpy(me.set().m.heapName, "system-uncached", me.v.flexCount()); } else { strncpy(me.set().m.heapName, "system", me.v.flexCount()); } me.set().m.allocFlags = 0; return C2R::Ok(); }; }; addParameter( DefineParam(mIonUsageInfo, "ion-usage") .withDefault(new C2StoreIonUsageInfo()) .withFields({ C2F(mIonUsageInfo, usage).flags({C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}), C2F(mIonUsageInfo, capacity).inRange(0, UINT32_MAX, 1024), C2F(mIonUsageInfo, heapMask).any(), C2F(mIonUsageInfo, allocFlags).flags({}), C2F(mIonUsageInfo, minAlignment).equalTo(0) }) .withSetter(Setter::setIonUsage) .build()); addParameter( DefineParam(mDmaBufUsageInfo, "dmabuf-usage") .withDefault(C2StoreDmaBufUsageInfo::AllocShared(0)) .withFields({ C2F(mDmaBufUsageInfo, m.usage).flags({C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}), C2F(mDmaBufUsageInfo, m.capacity).inRange(0, UINT32_MAX, 1024), C2F(mDmaBufUsageInfo, m.allocFlags).flags({}), C2F(mDmaBufUsageInfo, m.heapName).any(), }) .withSetter(Setter::setDmaBufUsage) .build()); } }; /** * Retrieves the component module for a component. * * \param module pointer to a shared_pointer where the component module will be stored on * success. * * \retval C2_OK the component loader has been successfully retrieved * \retval C2_NO_MEMORY not enough memory to locate the component loader * \retval C2_NOT_FOUND could not locate the component to be loaded * \retval C2_CORRUPTED the component loader could not be identified due to some modules being * corrupted (this can happen if the name does not refer to an already * identified component but some components could not be loaded due to * bad library) * \retval C2_REFUSED permission denied to find the component loader for the named component * (this can happen if the name does not refer to an already identified * component but some components could not be loaded due to lack of * permissions) */ c2_status_t findComponent(C2String name, std::shared_ptr *module); /** * Loads each component module and discover its contents. */ void visitComponents(); std::mutex mMutex; ///< mutex guarding the component lists during construction bool mVisited; ///< component modules visited std::map mComponents; ///< componentName -> component module std::vector> mComponentList; std::shared_ptr mReflector; Interface mInterface; }; c2_status_t C2RKComponentStore::ComponentModule::init(std::string componentName) { mLibHandle = dlopen(C2_RK_COMPONENT_PATH, RTLD_NOW | RTLD_NODELETE); LOG_ALWAYS_FATAL_IF(mLibHandle == nullptr, "could not dlopen %s: %s", C2_RK_COMPONENT_PATH, dlerror()); createFactory = (CreateRKCodec2FactoryFunc)dlsym(mLibHandle, "CreateRKCodec2Factory"); LOG_ALWAYS_FATAL_IF(createFactory == nullptr, "createFactory is null in %s", C2_RK_COMPONENT_PATH); destroyFactory = (DestroyRKCodec2FactoryFunc)dlsym(mLibHandle, "DestroyRKCodec2Factory"); LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr, "destroyFactory is null in %s", C2_RK_COMPONENT_PATH); mComponentFactory = createFactory(componentName); if (mComponentFactory == nullptr) { ALOGD("could not create factory in %s", C2_RK_COMPONENT_PATH); mInit = C2_NO_MEMORY; } else { mInit = C2_OK; } if (mInit != C2_OK) { return mInit; } std::shared_ptr intf; c2_status_t res = createInterface(0, &intf); if (res != C2_OK) { ALOGD("failed to create interface: %d", res); return mInit; } std::shared_ptr 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> 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 = 128; } 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; } C2RKComponentStore::ComponentModule::~ComponentModule() { if (destroyFactory && mComponentFactory) { destroyFactory(mComponentFactory); } if (mLibHandle) { ALOGV("unloading dll"); dlclose(mLibHandle); } } c2_status_t C2RKComponentStore::ComponentModule::createInterface( c2_node_id_t id, std::shared_ptr *interface, std::function deleter) { interface->reset(); if (mInit != C2_OK) { return mInit; } std::shared_ptr 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) }); return res; } c2_status_t C2RKComponentStore::ComponentModule::createComponent( c2_node_id_t id, std::shared_ptr *component, std::function deleter) { component->reset(); if (mInit != C2_OK) { return mInit; } std::shared_ptr 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) }); return res; } std::shared_ptr C2RKComponentStore::ComponentModule::getTraits() { std::unique_lock lock(mLock); return mTraits; } bool isHardwareSupport(C2String name) { int32_t coding = GetMppCodingFromComponentName(name); int32_t type = GetMppCtxTypeFromComponentName(name); if (!mpp_check_soc_cap((MppCtxType)type, (MppCodingType)coding)) { return false; } return true; } C2RKComponentStore::C2RKComponentStore() : mVisited(false), mReflector(std::make_shared()), mInterface(mReflector) { auto emplace = [this](const char *componentName) { mComponents.emplace(componentName, componentName); }; for (int i = 0; i < sComponentMapsSize; ++i) { if (isHardwareSupport(sComponentMaps[i].name)) { ALOGD("plugin %s", sComponentMaps[i].name.c_str()); emplace(sComponentMaps[i].name.c_str()); } } } c2_status_t C2RKComponentStore::copyBuffer( std::shared_ptr src, std::shared_ptr dst) { (void)src; (void)dst; return C2_OMITTED; } c2_status_t C2RKComponentStore::query_sm( const std::vector &stackParams, const std::vector &heapParamIndices, std::vector> *const heapParams) const { return mInterface.query(stackParams, heapParamIndices, C2_MAY_BLOCK, heapParams); } c2_status_t C2RKComponentStore::config_sm( const std::vector ¶ms, std::vector> *const failures) { return mInterface.config(params, C2_MAY_BLOCK, failures); } void C2RKComponentStore::visitComponents() { std::lock_guard lock(mMutex); if (mVisited) { return; } for (auto &nameAndLoader : mComponents) { ComponentLoader &loader = nameAndLoader.second; std::shared_ptr module; if (loader.fetchModule(&module) == C2_OK) { std::shared_ptr traits = module->getTraits(); if (traits) { mComponentList.push_back(traits); } } } mVisited = true; } std::vector> C2RKComponentStore::listComponents() { // This method SHALL return within 500ms. visitComponents(); return mComponentList; } c2_status_t C2RKComponentStore::findComponent( C2String name, std::shared_ptr *module) { (*module).reset(); visitComponents(); auto pos = mComponents.find(name); if (pos != mComponents.end()) { return pos->second.fetchModule(module); } return C2_NOT_FOUND; } c2_status_t C2RKComponentStore::createComponent( C2String name, std::shared_ptr *const component) { // This method SHALL return within 100ms. component->reset(); std::shared_ptr 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 C2RKComponentStore::createInterface( C2String name, std::shared_ptr *const interface) { // This method SHALL return within 100ms. interface->reset(); std::shared_ptr 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 C2RKComponentStore::querySupportedParams_nb( std::vector> *const params) const { return mInterface.querySupportedParams(params); } c2_status_t C2RKComponentStore::querySupportedValues_sm( std::vector &fields) const { return mInterface.querySupportedValues(fields, C2_MAY_BLOCK); } C2String C2RKComponentStore::getName() const { return "android.componentStore.rockchip"; } std::shared_ptr C2RKComponentStore::getParamReflector() const { return mReflector; } std::shared_ptr GetCodec2RKComponentStore() { static std::mutex mutex; static std::weak_ptr platformStore; std::lock_guard lock(mutex); std::shared_ptr store = platformStore.lock(); if (store == nullptr) { store = std::make_shared(); platformStore = store; } return store; } } // namespace android