在这一篇文章中,我们将先了解SW ComponentStore HIDL层的Codec2组件创建过程,然后了解组件实现架构。
1、C2PlatformComponentStore::createComponent
ComponentStore的HIDL接口会调用C2ComponentStore的createComponent方法:
c2_status_t C2PlatformComponentStore::createComponent(
C2String name, std::shared_ptr<C2Component> *const component) {
component->reset();
// 查找ComponentModule
std::shared_ptr<ComponentModule> module;
c2_status_t res = findComponent(name, &module);
if (res == C2_OK) {
// 创建C2Component实例
res = module->createComponent(0, component);
}
return res;
}
createComponent的传入参数为组件名称,传出参数为创建出的C2Component对象:
- 根据传入的组件名查找对应的ComponentModule;
- 调用ComponentModule的createComponent方法创建组件。
ComponentModule是ComponentLoader的一个成员,在Codec2服务启动一文,我们看到C2PlatformComponentStore的构造函数中会为每一个组件都添加一个ComponentLoader,此时ComponentModule还没有被初始化。
c2_status_t C2PlatformComponentStore::findComponent(
C2String name, std::shared_ptr<ComponentModule> *module) {
(*module).reset();
// 1.
visitComponents();
// 2.
auto pos = mComponentNameToPath.find(name);
if (pos != mComponentNameToPath.end()) {
// 3.
return mComponents.at(pos->second).fetchModule(module);
}
return C2_NOT_FOUND;
}
findComponent的工作如下:
- 首先调用visitComponents打开lib,加载函数指针,获取组件信息;
- 从mComponentNameToPath查找是否有匹配的组件名称;
- 调用ComponentLoader的fetchModule方法。
C2PlatformComponentStore使用visitComponents来打开lib,将handle以及获取到的函数指针存储到ComponentModule中,使用mVisited来保证lib信息的加载只会执行一次,visitComponents会遍历所有的lib,一次性载入所有组件的信息。但是要注意的是,visitComponents只会调用一次不代表ComponentModule存储的内容会一直存在,组件使用释放后,ComponentModule中的信息就会清空,直到下次使用再重新加载到ComponentModule中。
void C2PlatformComponentStore::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;
// 初始化ComponentModule
if (loader.fetchModule(&module) == C2_OK) {
// 获取traits
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;
}
c2_status_t 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) {
// 实例化ComponentModule
localModule = std::make_shared<ComponentModule>();
// 初始化ComponentModule
res = localModule->init(mLibPath);
if (res == C2_OK) {
mModule = localModule;
}
}
*module = localModule;
return res;
}
最终调用到ComponentModule的init方法来载入组件信息,传入参数为lib名:
c2_status_t C2PlatformComponentStore::ComponentModule::init(
std::string libPath) {
// 1. 打开lib,获取函数指针
if(!createFactory) {
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());
}
// 2. 创建C2ComponentFactory
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;
}
// 3. 创建Interface
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;
}
// 4. 获取组件traits
std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) C2Component::Traits);
if (traits) {
if (!C2InterfaceUtils::FillTraitsFromInterface(traits.get(), intf)) {
ALOGD("Failed to fill traits from interface");
return mInit;
}
// TODO: get this properly from the store during emplace
switch (traits->domain) {
case C2Component::DOMAIN_AUDIO:
traits->rank = 8;
break;
default:
traits->rank = 512;
}
}
mTraits = traits;
return mInit;
}
ComponentModule和OMXStore的内部实现类似,都是用的插件式编程。每个组件都要定义一个CreateCodec2Factory和DestroyCodec2Factory方法,ComponentModule使用dlopen打开lib后,再用dlsym获取到组件定义的方法,接着使用拿到的CreateCodec2Factory创建一个C2ComponentFactory对象,最后就能够使用C2ComponentFactory创建组件实例,或者创建接口实例了。
lib信息获取完成后,会调用C2ComponentFactory实例的createInterface方法,获取组件Traits。在init方法中偷偷修改了traits的rank,如果是audio组件,则修改rank为8。如果是video组件,修改rank为512。