From decfca19e5f4fea1d51bd0ca119bfc8e73dd3777 Mon Sep 17 00:00:00 2001 From: Hirokazu Honda <hiroh@google.com> Date: Tue, 23 Jan 2018 01:59:09 +0900 Subject: [PATCH] Implement component factory to load v4l2_codec2 as dynamic-linked lib Bug: 72354012 Test: setprop debug.stagefright.ccodec_v4l2 true Test: stagefright -S -N c2.v4l2.h264.decoder bear.mp4 Change-Id: I24c4595fbbd7b085ab37a99183433e422242b555 --- Android.bp | 20 ++ C2VDAComponent.cpp | 129 ++++-------- C2VDAComponentStore.cpp | 372 +++++++++++++++++++++++++++++++++++ include/C2V4l2Support.h | 21 ++ include/C2VDAComponent.h | 39 ---- tests/C2VDACompIntf_test.cpp | 31 ++- 6 files changed, 469 insertions(+), 143 deletions(-) create mode 100644 Android.bp create mode 100644 C2VDAComponentStore.cpp create mode 100644 include/C2V4l2Support.h diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..d92c553 --- /dev/null +++ b/Android.bp @@ -0,0 +1,20 @@ +cc_library_shared { + name: "libv4l2_c2componentstore", + srcs: [ + "C2VDAComponentStore.cpp", + ], + shared_libs: [ + "libstagefright_codec2", + "libstagefright_codec2_vndk", + "liblog", + "libutils", + ], + cflags: [ + "-Werror", + "-Wall", + "-std=c++14", + ], + export_include_dirs: [ + "include", + ], +} diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp index ec3bc9a..84d0d2b 100644 --- a/C2VDAComponent.cpp +++ b/C2VDAComponent.cpp @@ -17,6 +17,8 @@ #include <videodev2.h> +#include <C2PlatformSupport.h> + #include <base/bind.h> #include <base/bind_helpers.h> @@ -1299,106 +1301,57 @@ void C2VDAComponent::reportError(c2_status_t error) { mListener->onError_nb(shared_from_this(), reported_error); } -//////////////////////////////////////////////////////////////////////////////// -// Neglect flexible flag while matching parameter indices. -#define CASE(paramType) \ - case paramType::CORE_INDEX: \ - return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor{ \ - paramType::CORE_INDEX, \ - paramType::FIELD_LIST, \ - }) - -class C2VDAComponentStore::ParamReflector : public C2ParamReflector { +class C2VDAComponentFactory : public C2ComponentFactory { public: - virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) override { - switch (coreIndex.coreIndex()) { - //CASE(C2ComponentDomainInfo); //TODO: known codec2 framework bug - CASE(C2StreamFormatConfig); - CASE(C2VideoSizeStreamInfo); - CASE(C2PortMimeConfig); - CASE(C2MaxVideoSizeHintPortSetting); - } - return nullptr; - } -}; - -#undef CASE - -// TODO(johnylin): implement C2VDAComponentStore -C2VDAComponentStore::C2VDAComponentStore() : mParamReflector(std::make_shared<ParamReflector>()) {} - -C2String C2VDAComponentStore::getName() const { - return "android.componentStore.v4l2"; -} + C2VDAComponentFactory(C2String decoderName) : mDecoderName(decoderName){}; -c2_status_t C2VDAComponentStore::createComponent(C2String name, - std::shared_ptr<C2Component>* const component) { - UNUSED(name); - UNUSED(component); - return C2_OMITTED; -} - -c2_status_t C2VDAComponentStore::createInterface( - C2String name, std::shared_ptr<C2ComponentInterface>* const interface) { - interface->reset(new C2VDAComponentIntf(name, 12345)); - return C2_OK; -} - -std::vector<std::shared_ptr<const C2Component::Traits>> C2VDAComponentStore::listComponents() { - return std::vector<std::shared_ptr<const C2Component::Traits>>(); -} - -c2_status_t C2VDAComponentStore::copyBuffer(std::shared_ptr<C2GraphicBuffer> src, - std::shared_ptr<C2GraphicBuffer> dst) { - UNUSED(src); - UNUSED(dst); - return C2_OMITTED; -} + c2_status_t createComponent(c2_node_id_t id, std::shared_ptr<C2Component>* const component, + ComponentDeleter deleter) override { + UNUSED(deleter); + *component = std::shared_ptr<C2Component>(new C2VDAComponent(mDecoderName, id)); + return C2_OK; + } + c2_status_t createInterface(c2_node_id_t id, + std::shared_ptr<C2ComponentInterface>* const interface, + InterfaceDeleter deleter) override { + UNUSED(deleter); + *interface = + std::shared_ptr<C2ComponentInterface>(new C2VDAComponentIntf(mDecoderName, id)); + return C2_OK; + } + ~C2VDAComponentFactory() override = default; -std::shared_ptr<C2ParamReflector> C2VDAComponentStore::getParamReflector() const { - return mParamReflector; -} +private: + const C2String mDecoderName; +}; +} // namespace android -c2_status_t C2VDAComponentStore::querySupportedParams_nb( - std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const { - UNUSED(params); - return C2_OMITTED; +extern "C" ::android::C2ComponentFactory* CreateC2VDAH264Factory() { + ALOGV("in %s", __func__); + return new ::android::C2VDAComponentFactory(android::kH264DecoderName); } -c2_status_t C2VDAComponentStore::querySupportedValues_sm( - std::vector<C2FieldSupportedValuesQuery>& fields) const { - UNUSED(fields); - return C2_OMITTED; +extern "C" void DestroyC2VDAH264Factory(::android::C2ComponentFactory* factory) { + ALOGV("in %s", __func__); + delete factory; } -c2_status_t C2VDAComponentStore::query_sm( - const std::vector<C2Param*>& stackParams, - const std::vector<C2Param::Index>& heapParamIndices, - std::vector<std::unique_ptr<C2Param>>* const heapParams) const { - UNUSED(stackParams); - UNUSED(heapParamIndices); - UNUSED(heapParams); - return C2_OMITTED; +extern "C" ::android::C2ComponentFactory* CreateC2VDAVP8Factory() { + ALOGV("in %s", __func__); + return new ::android::C2VDAComponentFactory(android::kVP8DecoderName); } -c2_status_t C2VDAComponentStore::config_sm( - const std::vector<C2Param*>& params, - std::vector<std::unique_ptr<C2SettingResult>>* const failures) { - UNUSED(params); - UNUSED(failures); - return C2_OMITTED; +extern "C" void DestroyC2VDAVP8Factory(::android::C2ComponentFactory* factory) { + ALOGV("in %s", __func__); + delete factory; } -} // namespace android - -// ---------------------- Factory Functions Interface ---------------- - -using namespace android; - -extern "C" C2ComponentStore* create_store() { - return new C2VDAComponentStore(); +extern "C" ::android::C2ComponentFactory* CreateC2VDAVP9Factory() { + ALOGV("in %s", __func__); + return new ::android::C2VDAComponentFactory(android::kVP9DecoderName); } -extern "C" void destroy_store(C2ComponentStore* store) { - delete store; +extern "C" void DestroyC2VDAVP9Factory(::android::C2ComponentFactory* factory) { + ALOGV("in %s", __func__); + delete factory; } diff --git a/C2VDAComponentStore.cpp b/C2VDAComponentStore.cpp new file mode 100644 index 0000000..970aa72 --- /dev/null +++ b/C2VDAComponentStore.cpp @@ -0,0 +1,372 @@ +// 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 <C2Component.h> +#include <C2ComponentFactory.h> +#include <C2V4l2Support.h> + +#include <utils/Log.h> + +#include <dlfcn.h> + +#include <map> +#include <memory> +#include <mutex> + +#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<C2Component>* const component) override; + c2_status_t createInterface(C2String name, + std::shared_ptr<C2ComponentInterface>* const interface) override; + std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override; + c2_status_t copyBuffer(std::shared_ptr<C2GraphicBuffer> src, + std::shared_ptr<C2GraphicBuffer> dst) override; + c2_status_t query_sm(const std::vector<C2Param*>& stackParams, + const std::vector<C2Param::Index>& heapParamIndices, + std::vector<std::unique_ptr<C2Param>>* const heapParams) const override; + c2_status_t config_sm(const std::vector<C2Param*>& params, + std::vector<std::unique_ptr<C2SettingResult>>* const failures) override; + std::shared_ptr<C2ParamReflector> getParamReflector() const override; + c2_status_t querySupportedParams_nb( + std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const override; + c2_status_t querySupportedValues_sm( + std::vector<C2FieldSupportedValuesQuery>& 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) {} + ~ComponentModule() override; + c2_status_t init(std::string libPath, C2VDACodec codec); + + // Return the traits of the component in this module. + std::shared_ptr<const C2Component::Traits> getTraits(); + + // The implementation of C2ComponentFactory. + c2_status_t createComponent( + c2_node_id_t id, std::shared_ptr<C2Component>* component, + ComponentDeleter deleter = std::default_delete<C2Component>()) override; + c2_status_t createInterface( + c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* interface, + InterfaceDeleter deleter = std::default_delete<C2ComponentInterface>()) override; + + protected: + std::recursive_mutex mLock; ///< lock protecting mTraits + std::shared_ptr<C2Component::Traits> 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<ComponentModule>* module); + + private: + std::mutex mMutex; ///< mutex guarding the module + std::weak_ptr<ComponentModule> 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<C2String, ComponentLoader> 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<const C2Component::Traits> C2VDAComponentStore::ComponentModule::getTraits() { + std::unique_lock<std::recursive_mutex> lock(mLock); + if (!mTraits) { + std::shared_ptr<C2ComponentInterface> intf; + auto res = createInterface(0, &intf); + if (res != C2_OK) { + return nullptr; + } + auto traits = std::make_shared<C2Component::Traits>(); + if (traits) { + // TODO: fill out Traits. + } + mTraits = traits; + } + return mTraits; +} + +c2_status_t C2VDAComponentStore::ComponentModule::createComponent( + c2_node_id_t id, std::shared_ptr<C2Component>* component, + std::function<void(::android::C2Component*)> 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<C2ComponentInterface>* interface, + std::function<void(::android::C2ComponentInterface*)> 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<ComponentModule>* module) { + c2_status_t res = C2_OK; + std::lock_guard<std::mutex> lock(mMutex); + std::shared_ptr<ComponentModule> localModule = mModule.lock(); + if (localModule == nullptr) { + localModule = std::make_shared<ComponentModule>(); + 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.v4l2.h264.decoder"), + std::forward_as_tuple("libv4l2_codec2.so", C2VDACodec::H264)); + mComponents.emplace(std::piecewise_construct, std::forward_as_tuple("c2.v4l2.vp8.decoder"), + std::forward_as_tuple("libv4l2_codec2.so", C2VDACodec::VP8)); + mComponents.emplace(std::piecewise_construct, std::forward_as_tuple("c2.v4l2.vp9.decoder"), + std::forward_as_tuple("libv4l2_codec2.so", C2VDACodec::VP9)); +} + +C2String C2VDAComponentStore::getName() const { + return "android.componentStore.vda"; +} + +std::vector<std::shared_ptr<const C2Component::Traits>> C2VDAComponentStore::listComponents() { + // This method SHALL return within 500ms. + std::vector<std::shared_ptr<const C2Component::Traits>> list; + for (auto& it : mComponents) { + ComponentLoader& loader = it.second; + std::shared_ptr<ComponentModule> module; + c2_status_t res = loader.fetchModule(&module); + if (res == C2_OK) { + std::shared_ptr<const C2Component::Traits> 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<C2Component>* 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<ComponentModule> 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<C2ComponentInterface>* 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<ComponentModule> 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<C2GraphicBuffer> src, + std::shared_ptr<C2GraphicBuffer> dst) { + UNUSED(src); + UNUSED(dst); + return C2_OMITTED; +} + +c2_status_t C2VDAComponentStore::query_sm( + const std::vector<C2Param*>& stackParams, + const std::vector<C2Param::Index>& heapParamIndices, + std::vector<std::unique_ptr<C2Param>>* 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<C2Param*>& params, + std::vector<std::unique_ptr<C2SettingResult>>* const failures) { + // there are no supported configs + UNUSED(failures); + return params.empty() ? C2_OK : C2_BAD_INDEX; +} + +std::shared_ptr<C2ParamReflector> C2VDAComponentStore::getParamReflector() const { + // TODO + return nullptr; +} + +c2_status_t C2VDAComponentStore::querySupportedParams_nb( + std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const { + // there are no supported config params + UNUSED(params); + return C2_OK; +} + +c2_status_t C2VDAComponentStore::querySupportedValues_sm( + std::vector<C2FieldSupportedValuesQuery>& fields) const { + // there are no supported config params + return fields.empty() ? C2_OK : C2_BAD_INDEX; +} + +std::shared_ptr<C2ComponentStore> GetCodec2VDAComponentStore() { + 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) { + store = std::make_shared<C2VDAComponentStore>(); + platformStore = store; + } + return store; +} + +} // namespace android diff --git a/include/C2V4l2Support.h b/include/C2V4l2Support.h new file mode 100644 index 0000000..2f1d056 --- /dev/null +++ b/include/C2V4l2Support.h @@ -0,0 +1,21 @@ +// 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. + +#ifndef ANDROID_CODEC2_V4L2_SUPPORT_H +#define ANDROID_CODEC2_V4L2_SUPPORT_H + +#include <C2Component.h> + +#include <memory> + +namespace android { + +/** + * Returns the C2VDA component store. + * \retval nullptr if the platform component store could not be obtained + */ +std::shared_ptr<C2ComponentStore> GetCodec2VDAComponentStore(); +} // namespace android + +#endif // ANDROID_CODEC2_V4L2_SUPPORT_H diff --git a/include/C2VDAComponent.h b/include/C2VDAComponent.h index 22a83e3..9744259 100644 --- a/include/C2VDAComponent.h +++ b/include/C2VDAComponent.h @@ -321,45 +321,6 @@ private: DISALLOW_COPY_AND_ASSIGN(C2VDAComponent); }; -class C2VDAComponentStore : public C2ComponentStore { -public: - C2VDAComponentStore(); - ~C2VDAComponentStore() override {} - - C2String getName() const override; - - c2_status_t createComponent(C2String name, - std::shared_ptr<C2Component>* const component) override; - - c2_status_t createInterface(C2String name, - std::shared_ptr<C2ComponentInterface>* const interface) override; - - std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override; - - c2_status_t copyBuffer(std::shared_ptr<C2GraphicBuffer> src, - std::shared_ptr<C2GraphicBuffer> dst) override; - - std::shared_ptr<C2ParamReflector> getParamReflector() const override; - - c2_status_t querySupportedParams_nb( - std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const override; - - c2_status_t querySupportedValues_sm( - std::vector<C2FieldSupportedValuesQuery>& fields) const override; - - c2_status_t query_sm(const std::vector<C2Param*>& stackParams, - const std::vector<C2Param::Index>& heapParamIndices, - std::vector<std::unique_ptr<C2Param>>* const heapParams) const override; - - c2_status_t config_sm(const std::vector<C2Param*>& params, - std::vector<std::unique_ptr<C2SettingResult>>* const failures) override; - -private: - class ParamReflector; - - std::shared_ptr<C2ParamReflector> mParamReflector; -}; - } // namespace android #endif // ANDROID_C2_VDA_COMPONENT_H diff --git a/tests/C2VDACompIntf_test.cpp b/tests/C2VDACompIntf_test.cpp index dcfd0ac..402c1d8 100644 --- a/tests/C2VDACompIntf_test.cpp +++ b/tests/C2VDACompIntf_test.cpp @@ -413,20 +413,19 @@ void dumpStruct(const C2StructDescriptor& sd) { } // TODO: move this to some component store test -TEST_F(C2VDACompIntfTest, ParamReflector) { - std::shared_ptr<C2ComponentStore> store(new C2VDAComponentStore()); - - std::vector<std::shared_ptr<C2ParamDescriptor>> params; - - ASSERT_EQ(mIntf->querySupportedParams_nb(¶ms), C2_OK); - for (const auto& paramDesc : params) { - printf("name: %s\n", paramDesc->name().c_str()); - printf(" required: %s\n", paramDesc->isRequired() ? "yes" : "no"); - printf(" type: %x\n", paramDesc->index().type()); - std::unique_ptr<C2StructDescriptor> desc{ - store->getParamReflector()->describe(paramDesc->index().type())}; - if (desc.get()) dumpStruct(*desc); - } -} - +// TEST_F(C2VDACompIntfTest, ParamReflector) { +// std::shared_ptr<C2ComponentStore> store(new C2VDAComponentStore()); + +// std::vector<std::shared_ptr<C2ParamDescriptor>> params; + +// ASSERT_EQ(mIntf->querySupportedParams_nb(¶ms), C2_OK); +// for (const auto& paramDesc : params) { +// printf("name: %s\n", paramDesc->name().c_str()); +// printf(" required: %s\n", paramDesc->isRequired() ? "yes" : "no"); +// printf(" type: %x\n", paramDesc->index().type()); +// std::unique_ptr<C2StructDescriptor> desc{ +// store->getParamReflector()->describe(paramDesc->index().type())}; +// if (desc.get()) dumpStruct(*desc); +// } +// } } // namespace android -- GitLab