必读:Android Media Framework - 开篇
OpenMAX IL Spec阅读到上一节就结束了,这一节开始正式进入到Framework阅读阶段,我们将了解OpenMAX框架是如何与Android Framework连接的。
1、插件式编程
插件式编程(Plugin-based Programming)是一种软件开发模式,它允许开发者通过编写独立的、可插拔的模块(称为插件)来扩展应用程序的功能,而无需修改应用程序的核心代码。这种模式使得软件具有高度可扩展性、可维护性和灵活性。
插件式编程的核心思想是将应用程序的核心功能与扩展功能分离开来。核心功能负责提供基本的、必要的服务,而扩展功能则通过插件的形式添加到应用程序中,以提供额外的、可选的功能。
如何实现插件式编程?
-
定义插件接口:定义插件需要实现的接口,明确应用程序调用插件的方式。
-
编写插件:根据接口规范编写插件代码,实现所需的功能。
-
加载和卸载插件:在应用程序中编写代码来动态地加载和卸载插件。
-
调用插件功能:在应用程序中通过接口调用插件提供的功能。
举一个简单的例子:
首先定义插件需要实现的接口PluginInterface:
// PluginInterface.h
#ifndef PLUGIN_INTERFACE_H
#define PLUGIN_INTERFACE_H
class PluginInterface {
public:
virtual void execute() = 0;
};
// 应用程序调用插件的方式
typedef PluginInterface* (*CreatePluginFunc)();
typedef void (*DestroyPluginFunc)(PluginInterface*);
#endif // PLUGIN_INTERFACE_H
接着实现两个插件,这里贴出插件1的代码:
// MyPlugin_1.cpp
#include <iostream>
#include "PluginInterface.h"
class MyPlugin1 : public PluginInterface {
public:
void execute() override {
std::cout << "MyPlugin 1 executing!" << std::endl;
}
};
extern "C" PluginInterface* createPlugin() {
std::cout << "create MyPlugin 1!" << std::endl;
return new MyPlugin1();
}
extern "C" void destroyPlugin(PluginInterface* plugin) {
std::cout << "delete MyPlugin 1!" << std::endl;
delete plugin;
}
注意extern "C",这是为了确保C++的name mangling(名称修饰)不会影响这些函数的名称,从而能够准确获取到动态库中的函数。
最后是主程序加载卸载插件、调用插件功能:
// main.cpp
#include <iostream>
#include <dlfcn.h>
#include "PluginInterface.h"
static int loadAndExec(const char *libname) {
void* handle = dlopen(libname, RTLD_LAZY);
if (!handle) {
std::cout << "Cannot open library : " << libname << std::endl;
return -1;
}
CreatePluginFunc createPlugin = (CreatePluginFunc)dlsym(handle, "createPlugin");
if (!createPlugin) {
std::cout << "Cannot load symbol 'createPlugin' of library "<< libname << std::endl;
dlclose(handle);
handle = NULL;
return -1;
}
DestroyPluginFunc destroyPlugin = (DestroyPluginFunc)dlsym(handle, "destroyPlugin");
if (!destroyPlugin) {
std::cout << "Cannot load symbol 'destroyPlugin' of library "<< libname << std::endl;
dlclose(handle);
handle = NULL;
return -1;
}
PluginInterface* plugin = createPlugin();
plugin->execute();
destroyPlugin(plugin);
plugin = NULL;
dlclose(handle);
handle = NULL;
return 0;
}
int main() {
loadAndExec("./libMyPlugin_1.so");
loadAndExec("./libMyPlugin_2.so");
return 0;
}
代码执行结果
./test
// create MyPlugin 1!
// MyPlugin 1 executing!
// delete MyPlugin 1!
// create MyPlugin 2!
// MyPlugin 2 executing!
// delete MyPlugin 2!
在该示例中,MyPlugin1和MyPlugin2分别被编译为libMyPlugin_1.so和libMyPlugin_2.so,使用这两个插件时要用dlopen打开动态库,用dlsym获取预定义的函数。如果想用动态链接的方式链接这两个lib,由于包含同名函数,编译时就会出错了。
代码下载:公众号后台回复PluginDemo。
2、OMXStore
为了让芯片厂商能够在不修改原生代码的情况下使用硬件编解码组件,Android采用了插件式编程技术来动态加载厂商的实现。
代码参考:frameworks/av/media/libstagefright/omx/OMXStore.cpp
OMXStore::OMXStore() {
// ......
addVendorPlugin();
addPlatformPlugin();
}
OMXStore的构造函数调用了两个方法:
addPlatformPlugin
:添加平台插件,平台指的是Android系统自带/内置的;addVendorPlugin
:添加厂商实现的插件,厂商指的各大芯片厂商,即OMX组件实现者;
void OMXStore::addVendorPlugin() {
addPlugin("libstagefrighthw.so");
}
void OMXStore::addPlatformPlugin() {
addPlugin("libstagefright_softomx_plugin.so");
}
展开两个方法可以看到,OMXStore想要加载两个lib,libstagefright_softomx_plugin.so
由Android平台提供,libstagefrighthw.so
由厂商实现。libstagefrighthw.so应该如何实现呢?带着这个问题往下看addPlugin:
void OMXStore::addPlugin(const char *libname) {
// 1.
if (::android::base::GetIntProperty("vendor.media.omx", int64_t(1)) == 0) {
return;
}
// 2. 打开lib
void *libHandle = android_load_sphal_library(libname, RTLD_NOW);
if (libHandle == NULL) {
return;
}
// 3. 获取lib中的createOMXPlugin函数指针
typedef OMXPluginBase *(*CreateOMXPluginFunc)();
CreateOMXPluginFunc createOMXPlugin =
(CreateOMXPluginFunc)dlsym(
libHandle, "createOMXPlugin");
if (!createOMXPlugin)
createOMXPlugin = (CreateOMXPluginFunc)dlsym(
libHandle, "_ZN7android15createOMXPluginEv");
// 4.
OMXPluginBase *plugin = nullptr;
if (createOMXPlugin) {
plugin = (*createOMXPlugin)();
}
// 5.
if (plugin) {
mPlugins.push_back({ plugin, libHandle });
// 6
addPlugin(plugin);
} else {
android_unload_sphal_library(libHandle);
}
}