// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //#define LOG_NDEBUG 0 #define LOG_TAG "C2VDAComponentStore" #include #include #include #include #include #include #include #include #include #define UNUSED(expr) \ do { \ (void)(expr); \ } while (0) namespace android { class C2VDAComponentStore : public C2ComponentStore { public: C2VDAComponentStore(); ~C2VDAComponentStore() override = default; // The implementation of C2ComponentStore. C2String getName() const override; c2_status_t createComponent(C2String name, std::shared_ptr* const component) override; c2_status_t createInterface(C2String name, std::shared_ptr* const interface) override; std::vector> listComponents() override; c2_status_t copyBuffer(std::shared_ptr src, std::shared_ptr dst) override; c2_status_t query_sm(const std::vector& stackParams, const std::vector& heapParamIndices, std::vector>* const heapParams) const override; c2_status_t config_sm(const std::vector& params, std::vector>* const failures) override; std::shared_ptr getParamReflector() const override; c2_status_t querySupportedParams_nb( std::vector>* const params) const override; c2_status_t querySupportedValues_sm( std::vector& fields) const override; private: enum class C2VDACodec { UNKNOWN, H264, VP8, VP9, }; /** * 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 */ class ComponentModule : public C2ComponentFactory { public: ComponentModule() : mInit(C2_NO_INIT), mLibHandle(nullptr), createFactory(nullptr), destroyFactory(nullptr), mComponentFactory(nullptr) { } ~ComponentModule() override; c2_status_t init(std::string libPath, C2VDACodec codec); // Return the traits of the component in this module. std::shared_ptr getTraits(); // The implementation of C2ComponentFactory. c2_status_t createComponent( c2_node_id_t id, std::shared_ptr* component, ComponentDeleter deleter = std::default_delete()) override; c2_status_t createInterface( c2_node_id_t id, std::shared_ptr* interface, InterfaceDeleter deleter = std::default_delete()) override; 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 C2ComponentFactory::CreateCodec2FactoryFunc createFactory; ///< loaded create function C2ComponentFactory::DestroyCodec2FactoryFunc destroyFactory; ///< loaded destroy function C2ComponentFactory* mComponentFactory; ///< loaded/created component factory }; /** * An object encapsulating a loadable component module. * * \todo make this also work for enumerations */ class ComponentLoader { public: ComponentLoader(std::string libPath, C2VDACodec codec) : mLibPath(libPath), mCodec(codec) {} /** * 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); private: std::mutex mMutex; ///< mutex guarding the module std::weak_ptr mModule; ///< weak reference to the loaded module std::string mLibPath; ///< library path (or name) C2VDACodec mCodec = C2VDACodec::UNKNOWN; }; c2_status_t findComponent(C2String name, ComponentLoader** loader); std::map mComponents; ///< list of components }; C2VDAComponentStore::ComponentModule::~ComponentModule() { ALOGV("in %s", __func__); if (destroyFactory && mComponentFactory) { destroyFactory(mComponentFactory); } if (mLibHandle) { ALOGV("unloading dll"); dlclose(mLibHandle); } } c2_status_t C2VDAComponentStore::ComponentModule::init(std::string libPath, C2VDACodec codec) { ALOGV("in %s", __func__); ALOGV("loading dll"); mLibHandle = dlopen(libPath.c_str(), RTLD_NOW | RTLD_NODELETE); if (mLibHandle == nullptr) { ALOGD("could not dlopen %s: %s", libPath.c_str(), dlerror()); mInit = C2_CORRUPTED; } else { std::string createFactoryName; std::string destroyFactoryName; switch (codec) { case C2VDACodec::H264: createFactoryName = "CreateC2VDAH264Factory"; destroyFactoryName = "DestroyC2VDAH264Factory"; break; case C2VDACodec::VP8: createFactoryName = "CreateC2VDAVP8Factory"; destroyFactoryName = "DestroyC2VDAVP8Factory"; break; case C2VDACodec::VP9: createFactoryName = "CreateC2VDAVP9Factory"; destroyFactoryName = "DestroyC2VDAVP9Factory"; break; default: ALOGE("Unknown "); return C2_CORRUPTED; } createFactory = (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym( mLibHandle, createFactoryName.c_str()); destroyFactory = (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym( mLibHandle, destroyFactoryName.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; } } return mInit; } std::shared_ptr C2VDAComponentStore::ComponentModule::getTraits() { std::unique_lock lock(mLock); if (!mTraits) { std::shared_ptr intf; auto res = createInterface(0, &intf); if (res != C2_OK) { ALOGE("failed to create interface: %d", res); return nullptr; } std::shared_ptr traits(new (std::nothrow) C2Component::Traits); if (traits) { traits->name = intf->getName(); // TODO: get this from interface properly. bool encoder = (traits->name.find("encoder") != std::string::npos); uint32_t mediaTypeIndex = encoder ? C2PortMimeConfig::output::PARAM_TYPE : C2PortMimeConfig::input::PARAM_TYPE; std::vector> params; res = intf->query_vb({}, { mediaTypeIndex }, C2_MAY_BLOCK, ¶ms); if (res != C2_OK) { ALOGE("failed to query interface: %d", res); return nullptr; } if (params.size() != 1u) { ALOGE("failed to query interface: unexpected vector size: %zu", params.size()); return nullptr; } C2PortMimeConfig *mediaTypeConfig = (C2PortMimeConfig *)(params[0].get()); if (mediaTypeConfig == nullptr) { ALOGE("failed to query media type"); return nullptr; } traits->mediaType = mediaTypeConfig->m.value; // TODO: get this properly. // Set the rank prior to c2.android.* (=0x200) and after OMX.google.* (=0x100) by now. // In the future this should be prior to OMX.google.* as well so that ARC HW codec // would be the first priority. traits->rank = 0x180; } mTraits = traits; } return mTraits; } c2_status_t C2VDAComponentStore::ComponentModule::createComponent( c2_node_id_t id, std::shared_ptr* component, std::function deleter) { UNUSED(deleter); component->reset(); if (mInit != C2_OK) { return mInit; } return mComponentFactory->createComponent(id, component, C2ComponentFactory::ComponentDeleter()); } c2_status_t C2VDAComponentStore::ComponentModule::createInterface( c2_node_id_t id, std::shared_ptr* interface, std::function deleter) { UNUSED(deleter); interface->reset(); if (mInit != C2_OK) { return mInit; } return mComponentFactory->createInterface(id, interface, C2ComponentFactory::InterfaceDeleter()); } c2_status_t C2VDAComponentStore::ComponentLoader::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(mLibPath, mCodec); if (res == C2_OK) { mModule = localModule; } } *module = localModule; return res; } C2VDAComponentStore::C2VDAComponentStore() { // TODO: move this also into a .so so it can be updated mComponents.emplace(std::piecewise_construct, std::forward_as_tuple("c2.vda.avc.decoder"), std::forward_as_tuple("libv4l2_codec2.so", C2VDACodec::H264)); mComponents.emplace(std::piecewise_construct, std::forward_as_tuple("c2.vda.vp8.decoder"), std::forward_as_tuple("libv4l2_codec2.so", C2VDACodec::VP8)); mComponents.emplace(std::piecewise_construct, std::forward_as_tuple("c2.vda.vp9.decoder"), std::forward_as_tuple("libv4l2_codec2.so", C2VDACodec::VP9)); } C2String C2VDAComponentStore::getName() const { return "android.componentStore.vda"; } std::vector> C2VDAComponentStore::listComponents() { // This method SHALL return within 500ms. std::vector> list; for (auto& it : mComponents) { ComponentLoader& loader = it.second; std::shared_ptr module; c2_status_t res = loader.fetchModule(&module); if (res == C2_OK) { std::shared_ptr traits = module->getTraits(); if (traits) { list.push_back(traits); } } } return list; } c2_status_t C2VDAComponentStore::findComponent(C2String name, ComponentLoader** loader) { *loader = nullptr; auto pos = mComponents.find(name); // TODO: check aliases if (pos == mComponents.end()) { return C2_NOT_FOUND; } *loader = &pos->second; return C2_OK; } c2_status_t C2VDAComponentStore::createComponent(C2String name, std::shared_ptr* const component) { // This method SHALL return within 100ms. component->reset(); ComponentLoader* loader; c2_status_t res = findComponent(name, &loader); if (res == C2_OK) { std::shared_ptr module; res = loader->fetchModule(&module); if (res == C2_OK) { // TODO: get a unique node ID res = module->createComponent(0, component); } } return res; } c2_status_t C2VDAComponentStore::createInterface( C2String name, std::shared_ptr* const interface) { // This method SHALL return within 100ms. interface->reset(); ComponentLoader* loader; c2_status_t res = findComponent(name, &loader); if (res == C2_OK) { std::shared_ptr module; res = loader->fetchModule(&module); if (res == C2_OK) { // TODO: get a unique node ID res = module->createInterface(0, interface); } } return res; } c2_status_t C2VDAComponentStore::copyBuffer(std::shared_ptr src, std::shared_ptr dst) { UNUSED(src); UNUSED(dst); return C2_OMITTED; } c2_status_t C2VDAComponentStore::query_sm( const std::vector& stackParams, const std::vector& heapParamIndices, std::vector>* const heapParams) const { // there are no supported configs UNUSED(heapParams); return stackParams.empty() && heapParamIndices.empty() ? C2_OK : C2_BAD_INDEX; } c2_status_t C2VDAComponentStore::config_sm( const std::vector& params, std::vector>* const failures) { // there are no supported configs UNUSED(failures); return params.empty() ? C2_OK : C2_BAD_INDEX; } std::shared_ptr C2VDAComponentStore::getParamReflector() const { // TODO return nullptr; } c2_status_t C2VDAComponentStore::querySupportedParams_nb( std::vector>* const params) const { // there are no supported config params UNUSED(params); return C2_OK; } c2_status_t C2VDAComponentStore::querySupportedValues_sm( std::vector& fields) const { // there are no supported config params return fields.empty() ? C2_OK : C2_BAD_INDEX; } std::shared_ptr GetCodec2VDAComponentStore() { 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