OMX框架层插件化创建、注册和实现处理流程源码分析
承接上一章节分析:Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 2】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
推荐涉及到的知识点:
Binder机制实现原理:Android C++底层Binder通信机制原理分析总结【通俗易懂】
ALooper机制实现原理:Android native层媒体通信架构AHandler/ALooper机制实现源码分析
Binder异常关闭监听:Android native层DeathRecipient对关联进程(如相关Service服务进程)异常关闭通知事件的监听实现源码分析
【本章节小节序号将重新开始】
Omx 类声明和构造函数实现:
Omx 类声明【省略其他无关代码】
备注:关于HIDL语言大致实现机制可参考上一章节中的分析。
// [frameworks/av/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h]
// 引入由HIDL语言实现的头文件编译自动生成的对应C++头文件
#include <android/hardware/media/omx/1.0/IOmx.h>
namespace hardware {
namespace media {
namespace omx {
namespace V1_0 {
namespace implementation {
using ::android::hardware::media::omx::V1_0::IOmx;
// 此服务还实现了Binder死亡监听功能,不过它是HIDL实现的
struct Omx : public IOmx, public hidl_death_recipient {
Omx();
virtual ~Omx();
protected:
OMXMaster* mMaster;
Mutex mLock;
KeyedVector<wp<IBase>, sp<OMXNodeInstance> > mLiveNodes;
KeyedVector<OMXNodeInstance*, wp<IBase> > mNode2Observer;
MediaCodecsXmlParser mParser;
}
// 注意此处此时还提供了直接通过加载该so库寻找【HIDL_FETCH_IOmx】名称的方法指针即可创建该服务对象指针
// 当然此处暂不分析
extern "C" IOmx* HIDL_FETCH_IOmx(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace omx
} // namespace media
} // namespace hardware
} // namespace android
Omx 类构造函数实现
// [frameworks/av/media/libstagefright/omx/1.0/Omx.cpp]
Omx::Omx() :
// 指针指向new的一个空对象
// 见第1小节分析
mMaster(new OMXMaster()),
// mParser对象即MediaCodecsXmlParser类默认无参构造函数初始化
// 见第2小节分析
mParser() {
// 见第3小节分析
(void)mParser.parseXmlFilesInSearchDirs();
// 由上一章节中defaultProfilingResultsXmlPath参数的描述分析可知,
// 该路径文件目前是未启用的,因此暂不分析该实现,其实际实现也是解析xml数据信息
(void)mParser.parseXmlPath(mParser.defaultProfilingResultsXmlPath);
}
1、OMXMaster类声明和构造函数实现:
OMXMaster类声明【省略其他代码】
// [frameworks/av/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h]
struct OMXMaster : public OMXPluginBase {
OMXMaster();
virtual ~OMXMaster();
}
// 注意:该类是非常重要的,即它是所有系统编解码器插件实现的超类。因此此处将它所有声明给出。
// [native/headers/media_plugin/media/hardware/OMXPluginBase.h]
struct OMXPluginBase {
OMXPluginBase() {}
virtual ~OMXPluginBase() {}
// 该方法即为具体插件需要实现的获取指定音视频编解码格式的编解码器具体(实现)组件实例
// OMX_ERRORTYPE:OMX框架定义的错误码枚举类型,位于[native/headers/media_plugin/media/openmax/OMX_Core.h]
// OMX_CALLBACKTYPE:是个结构体,见下面的声明
// OMX_COMPONENTTYPE:是个结构体,见下面的声明
// OMX_PTR:typedef void* OMX_PTR; 即是一个无类型数据指针,指向任何的数据类型。
virtual OMX_ERRORTYPE makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component) = 0;
// 该方法即为释放不在使用的编解码器具体(实现)组件实例
virtual OMX_ERRORTYPE destroyComponentInstance(
OMX_COMPONENTTYPE *component) = 0;
// 枚举组件处理即循环读取OMX实现插件支持的所有音视频编解码器对应的组件名称。
// 备注:每次调用只会获取一个该插件实现的组件名。
// typedef char* OMX_STRING;
// typedef uint32_t OMX_U32;
virtual OMX_ERRORTYPE enumerateComponents(
OMX_STRING name,
size_t size,
OMX_U32 index) = 0;
// 获取其组件角色信息,也就是上一章节分析的节点角色信息
virtual OMX_ERRORTYPE getRolesOfComponent(
const char *name,
Vector<String8> *roles) = 0;
private:
OMXPluginBase(const OMXPluginBase &);
OMXPluginBase &operator=(const OMXPluginBase &);
};
OMX_CALLBACKTYPE声明:
如下,该结构声明可知,它是上层传给具体OMX插件实现者的回调类型结构,其作用正如它的三种类型方法指针,主要就是接收底层OMX插件编解码工作时的数据交互回调,通过方法指针可实现模块插件化自定义实现。后续会分析这些方法具体使用。
备注:英文注释此处是我去掉了的,毕竟太多了,有需要的可以自行在android源码在线查看的网站上搜索该文件就可以看到啦。
typedef struct OMX_CALLBACKTYPE
{
OMX_ERRORTYPE (*EventHandler)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData);
OMX_ERRORTYPE (*EmptyBufferDone)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*FillBufferDone)(
OMX_OUT OMX_HANDLETYPE hComponent,
OMX_OUT OMX_PTR pAppData,
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);
} OMX_CALLBACKTYPE;
OMX_COMPONENTTYPE声明:
该结构体其实就是对应 makeComponentInstance() 获取返回的指定音视频编解码格式的编解码器具体(实现)组件实例,可通过该组件实例访问句柄来访问插件中的具体实现。英文注释其实已经解释的非常清楚了,我就不翻译了,比较简单。另外成员变量和函数上面的英文注释此处是我去掉了的,毕竟太多了,有需要的可以自行在android源码在线查看的网站上搜索该文件就可以看到啦。
// [native/headers/media_plugin/media/openmax/OMX_Component.h]
/** The OMX_HANDLETYPE structure defines the component handle. The component
* handle is used to access all of the component's public methods and also
* contains pointers to the component's private data area. The component
* handle is initialized by the OMX core (with help from the component)
* during the process of loading the component. After the component is
* successfully loaded, the application can safely access any of the
* component's public functions (although some may return an error because
* the state is inappropriate for the access).
*
* @ingroup comp
*/
typedef struct OMX_COMPONENTTYPE
{
OMX_U32 nSize;
OMX_VERSIONTYPE nVersion;
OMX_PTR pComponentPrivate;
OMX_PTR pApplicationPrivate;
OMX_ERRORTYPE (*GetComponentVersion)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_STRING pComponentName,
OMX_OUT OMX_VERSIONTYPE* pComponentVersion,
OMX_OUT OMX_VERSIONTYPE* pSpecVersion,
OMX_OUT OMX_UUIDTYPE* pComponentUUID);
OMX_ERRORTYPE (*SendCommand)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_COMMANDTYPE Cmd,
OMX_IN OMX_U32 nParam1,
OMX_IN OMX_PTR pCmdData);
OMX_ERRORTYPE (*GetParameter)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nParamIndex,
OMX_INOUT OMX_PTR pComponentParameterStructure);
OMX_ERRORTYPE (*SetParameter)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_IN OMX_PTR pComponentParameterStructure);
OMX_ERRORTYPE (*GetConfig)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_INOUT OMX_PTR pComponentConfigStructure);
OMX_ERRORTYPE (*SetConfig)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_IN OMX_PTR pComponentConfigStructure);
OMX_ERRORTYPE (*GetExtensionIndex)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_STRING cParameterName,
OMX_OUT OMX_INDEXTYPE* pIndexType);
OMX_ERRORTYPE (*GetState)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_STATETYPE* pState);
OMX_ERRORTYPE (*ComponentTunnelRequest)(
OMX_IN OMX_HANDLETYPE hComp,
OMX_IN OMX_U32 nPort,
OMX_IN OMX_HANDLETYPE hTunneledComp,
OMX_IN OMX_U32 nTunneledPort,
OMX_INOUT OMX_TUNNELSETUPTYPE* pTunnelSetup);
OMX_ERRORTYPE (*UseBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN OMX_U32 nSizeBytes,
OMX_IN OMX_U8* pBuffer);
OMX_ERRORTYPE (*AllocateBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBuffer,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN OMX_U32 nSizeBytes);
OMX_ERRORTYPE (*FreeBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*EmptyThisBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*FillThisBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*SetCallbacks)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_CALLBACKTYPE* pCallbacks,
OMX_IN OMX_PTR pAppData);
OMX_ERRORTYPE (*ComponentDeInit)(
OMX_IN OMX_HANDLETYPE hComponent);
OMX_ERRORTYPE (*UseEGLImage)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN void* eglImage);
OMX_ERRORTYPE (*ComponentRoleEnum)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_U8 *cRole,
OMX_IN OMX_U32 nIndex);
} OMX_COMPONENTTYPE;
OMXMaster类构造函数实现
// [frameworks/av/media/libstagefright/omx/OMXMaster.cpp]
OMXMaster::OMXMaster() {
// 获取当前执行的进程ID
pid_t pid = getpid();
char filename[20];
// 获取名称格式为"/proc/%d/comm"格式化的文件名称,如"/proc/123/comm"。
// 该路径就是当前进程使用和打开的文件操作路径,linux中把所有资源都定义为文件来操作,
// 看它的命名可知, comm 应该是其公共使用的资源操作。
// 备注:关于该路径的作用可自行百度,也可以参考此前该系列分析章节中已有的说明。
// 【真实pad中每个进程也都会有该操作文件路径】
snprintf(filename, sizeof(filename), "/proc/%d/comm", pid);
// 打开该文件路径
int fd = open(filename, O_RDONLY);
if (fd < 0) {
// 打开失败,无法确定进程名称即进程号有误
ALOGW("couldn't determine process name");
strlcpy(mProcessName, "<unknown>", sizeof(mProcessName));
} else {
// 打开成功,则读取进程名
ssize_t len = read(fd, mProcessName, sizeof(mProcessName));
if (len < 2) {
// 进程名读取大小不应该小于2个字节
ALOGW("couldn't determine process name");
strlcpy(mProcessName, "<unknown>", sizeof(mProcessName));
} else {
// 进程名读取成功,mProcessName为char字符数组定义,因此必须最后一位char字符设置为0即空字符,
// 此时该数组才表示一个字符串,否则只是一个数组而已。【英文注释可知,最后一个字符其实是个换行符】
// the name is newline terminated, so erase the newline
mProcessName[len - 1] = 0;
}
// 关闭该文件操作
close(fd);
}
// 上面的处理其实就是为了获取当前进程名称
// 添加供应商编解码器插件实现【也就是供应商可自定义添加硬件编解码器】
// 见下面的分析
addVendorPlugin();
// 添加平台编解码器插件实现
// 备注:其实现和上面的相同,就是加载的so库不同。
addPlatformPlugin();
}
addVendorPlugin()实现分析:
添加供应商编解码器插件实现【也就是供应商可自定义添加硬件编解码器】
备注:该文件供应商是实现了的,比如高通添加的硬编解码器,在真实pad中路径为 [vendor/lib64/libstagefrighthw.so] 和[vendor/lib/libstagefrighthw.so]。
其(高通)实现模块为【hardware/qcom/media/libstagefrighthw/Android.mk】,但是需要注意该文件的版权阐述还是属于AOSP的。
因此若想要添加自己的硬编解码器可以参考该目录下的实现。
// [frameworks/av/media/libstagefright/omx/OMXMaster.cpp]
void OMXMaster::addVendorPlugin() {
// 见下面的分析
addPlugin("libstagefrighthw.so");
}
addPlatformPlugin()实现分析:
添加平台编解码器插件实现。【根据so库名称可知它应该是软编解码器插件实现】
和上面的分析一样,就是加载实现的so库不一样。
注意:该so库在真实pad中只存在于lib库中,而在lib64库中没有。即[vendor/lib/libstagefright_softomx_plugin.so],因此其实可以判断出该库只会启用32位库lib下面的so库。
其安卓默认实现模块为【frameworks/av/media/libstagefright/omx/Android.bp】,Android.bp文件类似以前的mk编译配置文件,这是安卓新编译配置文件。
// [frameworks/av/media/libstagefright/omx/OMXMaster.cpp]
void OMXMaster::addPlatformPlugin() {
addPlugin("libstagefright_softomx_plugin.so");
}
addPlugin(“libstagefrighthw.so”)实现分析:
添加供应商软硬编解码器插件实现【也就是供应商可自定义添加软硬编解码器】
void OMXMaster::addPlugin(const char *libname) {
// 该方法是安卓对linux打开so库方法封装处理后的方法
// 其头文件为【system/core/libvndksupport/include/vndksupport/linker.h】
// 其对应实现文件【system/core/libvndksupport/linker.c】
// 见1.1小节分析
void *libHandle = android_load_sphal_library(libname, RTLD_NOW);
if (libHandle == NULL) {
return;
}
// 打开成功时
// 声明OMX插件实现的该方法指针
typedef OMXPluginBase *(*CreateOMXPluginFunc)();
// 【由此前很多的分析可知】
// 根据国际惯例,此处即会先从该so库中加载名称为"createOMXPlugin"的方法开始(执行)地址,
// 此处加载的方法名一定是C方法名代码编译方式来编译的。
CreateOMXPluginFunc createOMXPlugin =
(CreateOMXPluginFunc)dlsym(
libHandle, "createOMXPlugin");
// 通过这两个so库的该方法实现分析可知,高通的实现是C++方法编译的,而libstagefright_softomx_plugin.so则是C方式编译的。
if (!createOMXPlugin)
// 若失败,则尝试加载C++方法名代码编译方式来编译的方法名。
createOMXPlugin = (CreateOMXPluginFunc)dlsym(
libHandle, "_ZN7android15createOMXPluginEv");
OMXPluginBase *plugin = nullptr;
if (createOMXPlugin) {
// 该方法访问指针获取成功时,执行它并缓存器返回参数即一个指向OMXPluginBase类型(可能为子类)的插件实现对象指针。
// 因此需要分析每个插件对应的createOMXPlugin该方法的具体实现
// 见1.2小节分析
plugin = (*createOMXPlugin)();
}
if (plugin) {
// 创建插件成功时
// 将该插件对象指针缓存在mPlugins插件列表集合全局变量中,此处添加的是匿名对象元素
// mPlugins的声明实现,见1.3小节分析
mPlugins.push_back({ plugin, libHandle });
// 添加插件实现对象指针
// 见1.4小节分析
addPlugin(plugin);
} else {
// 此方法不再分析,和上面打开so库对应,也就是关闭该so库。最终肯定也会调用系统方法dlclose()方法。
android_unload_sphal_library(libHandle);
}
}
1.1、android_load_sphal_library(libname, RTLD_NOW)实现分析:
// [system/core/libvndksupport/linker.c]
void* android_load_sphal_library(const char* name, int flag) {
// 见1.1.1小节分析
struct android_namespace_t* vendor_namespace = get_vendor_namespace();
if (vendor_namespace != NULL) {
// 获取vendor的命名空间so库路径成功时
// 动态库额外结构信息对象
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace,
};
void* handle = NULL;
// 此为方法指针,但其声明为extern引入外部实现,
// linker.c中声明为:__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
// 因此其实现文件通过搜索可知为[bionic/libdl/libdl.cpp]
// 其实根据方法名称就可知,该方法安卓对系统打开so库方法封装处理后的方法。
// 该方法和系统函数dlopen不同之处其实就是,它可以指定so库路径。
// 见1.1.2分析
if (android_dlopen_ext != NULL) {
// 若打开成功,则会返回当前指定名称so库的访问句柄操作指针
handle = android_dlopen_ext(name, flag, &dlextinfo);
}
if (!handle) {
ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror());
}
return handle;
} else {
// 获取失败时,则直接调用系统函数dlopen加载方式来打开该名称对应的so库,
// 它将会尝试去加载系统默认的几个so库路径下的对应so库,加载成功则返回,而上面的处理是指定加载某些路径下的so库。
ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
return dlopen(name, flag);
}
}
1.1.1、get_vendor_namespace()实现分析:
// [system/core/libvndksupport/linker.c]
static struct android_namespace_t* get_vendor_namespace() {
// 加载的命名空间数组【三个元素,最后一个为null】
const char* namespace_names[] = {"sphal", "default", NULL};
// android命令空间信息全局缓存变量
static struct android_namespace_t* vendor_namespace = NULL;
if (vendor_namespace == NULL) {
// 第一次需要初始化
int name_idx = 0;
while (namespace_names[name_idx] != NULL) {
// 此为方法指针,但其声明为extern引入外部实现,
// linker.c中声明为:__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
// 因此其实现文件通过搜索可知为[bionic/libdl/libdl_android.cpp]
// 见下面的分析
if (android_get_exported_namespace != NULL) {
// 因此如下分析可知,最后返回了包含有"/vendor/lib64"路径和"/vendor/lib"路径的android命名空间结构体信息,
// 因此本次需要加载的这两个so库将会加载成功。
vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
}
if (vendor_namespace != NULL) {
// 获取成功,则缓存匹配到的当前key值命名空间名称
namespace_name = namespace_names[name_idx];
break;
}
name_idx++;
}
}
// 返回匹配到的命名空间结构体信息
return vendor_namespace;
}
android_get_exported_namespace类型方法指针:
// [bionic/libdl/libdl_android.cpp]
__attribute__((__weak__))
struct android_namespace_t* android_get_exported_namespace(const char* name) {
return __loader_android_get_exported_namespace(name);
}
// [bionic/linker/dlfcn.cpp]
android_namespace_t* __loader_android_get_exported_namespace(const char* name) {
return get_exported_namespace(name);
}
// [bionic/linker/linker.cpp]
// This function finds a namespace exported in ld.config.txt by its name.
// A namespace can be exported by setting .visible property to true.
android_namespace_t* get_exported_namespace(const char* name) {
if (name == nullptr) {
return nullptr;
}
// 到此处即发现,其实是在一个全局缓存变量声明如下:
// static std::unordered_map<std::string, android_namespace_t*> g_exported_namespaces;
// 即是一个map对象中通过指定命名空间名来查询系统开机时初始化到该map映射对象的命名空间值,
// 该初始化是在当前文件的【init_default_namespace_no_config()】方法中,
// 加载在【init_default_namespaces()】方法中完成,并且设置了“default”名称的key值,
// 即也就是说当前map对象中有一个名为"default"的key值,其对应的参数如下,
// 其加载了当前系统需要加载的lib64目录,目前加载的lib64目录如下:
// static const char* const kSystemLibDir = "/system/lib64";
// static const char* const kOdmLibDir = "/odm/lib64";
// static const char* const kVendorLibDir = "/vendor/lib64";
// 注意:此时该全局缓存变量列表中还会加载"/vendor/lib"等32位库路径的。
// 因此此处将会在查询"default"的key值时返回包含这三个lib64路径和其他32位lib路径字符串的android_namespace_t结构体指针。
auto it = g_exported_namespaces.find(std::string(name));
if (it == g_exported_namespaces.end()) {
return nullptr;
}
return it->second;
}
1.1.2、android_dlopen_ext(name, flag, &dlextinfo)实现分析:
打开指定名称so库,返回当前指定名称so库的访问句柄操作指针。该方法安卓对系统打开so库方法封装处理后的方法
// [bionic/libdl/libdl.cpp]
__attribute__((__weak__))
void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo) {
const void* caller_addr = __builtin_return_address(0);
return __loader_android_dlopen_ext(filename, flag, extinfo, caller_addr);
}
// [bionic/linker/dlfcn.cpp]
void* __loader_android_dlopen_ext(const char* filename,
int flags,
const android_dlextinfo* extinfo,
const void* caller_addr) {
return dlopen_ext(filename, flags, extinfo, caller_addr);
}
// [bionic/linker/dlfcn.cpp]
static void* dlopen_ext(const char* filename,
int flags,
const android_dlextinfo* extinfo,
const void* caller_addr) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
g_linker_logger.ResetState();
// do_dlopen方法最终是在[bionic/linker/linker.cpp]文件中实现的,主要功能就是根据指定参数来打开指定so库并返回访问句柄指针。
// 注意,分析到此处就不在分析该方法内部的处理了,里面的处理非常复杂,
// 但是简单的总结就是:内部会根据传入的参数来创建【LoadTask】so库加载对象,其内部最终也是使用系统函数dlopen完成的。
void* result = do_dlopen(filename, flags, extinfo, caller_addr);
if (result == nullptr) {
__bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
return nullptr;
}
return result;
}
1.2、plugin = (*createOMXPlugin)(); 实现分析:
分析每个插件对应的createOMXPlugin该方法的具体实现【此处只分析lib64实现即libstagefrighthw.so,另外一个so库的实现大致类似的】libstagefrighthw.so 高通库实现分析:
注意:该方法实现是以C++方法名编译方式编译到so库的。
// [hardware/qcom/media/libstagefrighthw/QComOMXPlugin.cpp]
OMXPluginBase *createOMXPlugin() {
return new QComOMXPlugin;
}
QComOMXPlugin类声明:【省略部分代码】
其实现了每个OMX必须实现的超类OMXPluginBase,该类型前面已知晓。
// [hardware/qcom/media/libstagefrighthw/QComOMXPlugin.h]
struct QComOMXPlugin : public OMXPluginBase {
// 由下可知,自定义了一些方法指针及其变量
private:
void *mLibHandle;
typedef OMX_ERRORTYPE (*InitFunc)();
typedef OMX_ERRORTYPE (*DeinitFunc)();
typedef OMX_ERRORTYPE (*ComponentNameEnumFunc)(
OMX_STRING, OMX_U32, OMX_U32);
typedef OMX_ERRORTYPE (*GetHandleFunc)(
OMX_HANDLETYPE *, OMX_STRING, OMX_PTR, OMX_CALLBACKTYPE *);
typedef OMX_ERRORTYPE (*FreeHandleFunc)(OMX_HANDLETYPE *);
typedef OMX_ERRORTYPE (*GetRolesOfComponentFunc)(
OMX_STRING, OMX_U32 *, OMX_U8 **);
void freeArrayOfAllocatedMemory(OMX_U8 **array, OMX_S32 count);
InitFunc mInit;
DeinitFunc mDeinit;
ComponentNameEnumFunc mComponentNameEnum;
GetHandleFunc mGetHandle;
FreeHandleFunc mFreeHandle;
GetRolesOfComponentFunc mGetRolesOfComponentHandle;
}
// [native/headers/media_plugin/media/hardware/OMXPluginBase.h]
struct OMXPluginBase {
OMXPluginBase() {}
virtual ~OMXPluginBase() {}
}
QComOMXPlugin类构造函数实现:
由下面的实现可知,打开libOmxCore.so库,该so库是高通实现,位于【hardware/qcom/media/mm-core/Android.mk】,从该so库中加载初始化这些方法访问句柄指针。
备注:从下面的方法加载方式可以知道,这些方法名在该so库的方法名代码编译方式一定是C代码编译方式。
注意:下面的这些方法指针声明其实都是OMX框架插件中声明的,也是必须的实现,其声明文件为[native/headers/media_plugin/media/openmax/OMX_Core.h],而OMX框架插件需要实现的头文件都被高通复制了一份在mm-core的头文件夹下面。
关于libOmxCore.so内部的具体实现就不再分析了,因为后续会分析安卓原生自带的软编解码器插件库的实现即libstagefright_softomx_plugin.so。其实际实现原理都是类似的,高通实现库以后有时间考虑分析下吧。TODO
// [hardware/qcom/media/libstagefrighthw/QComOMXPlugin.cpp]
QComOMXPlugin::QComOMXPlugin()
: mLibHandle(dlopen("libOmxCore.so", RTLD_NOW)),
mInit(NULL),
mDeinit(NULL),
mComponentNameEnum(NULL),
mGetHandle(NULL),
mFreeHandle(NULL),
mGetRolesOfComponentHandle(NULL) {
if (mLibHandle != NULL) {
mInit = (InitFunc)dlsym(mLibHandle, "OMX_Init");
mDeinit = (DeinitFunc)dlsym(mLibHandle, "OMX_Deinit");
mComponentNameEnum =
(ComponentNameEnumFunc)dlsym(mLibHandle, "OMX_ComponentNameEnum");
mGetHandle = (GetHandleFunc)dlsym(mLibHandle, "OMX_GetHandle");
mFreeHandle = (FreeHandleFunc)dlsym(mLibHandle, "OMX_FreeHandle");
mGetRolesOfComponentHandle =
(GetRolesOfComponentFunc)dlsym(
mLibHandle, "OMX_GetRolesOfComponent");
if (!mInit || !mDeinit || !mComponentNameEnum || !mGetHandle ||
!mFreeHandle || !mGetRolesOfComponentHandle) {
// 只要有一个方法指针初始化失败,则关闭该so库
dlclose(mLibHandle);
mLibHandle = NULL;
} else
// 初始化全成功,则调用so库的初始化实现方法。
(*mInit)();
}
}
再分析安卓原生自带的软编解码器插件库【libstagefright_softomx_plugin.so】的 createOMXPlugin() 函数相关实现:
该方法实现是以C方法名编译方式编译到so库的。
// [frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp]
extern "C" OMXPluginBase* createOMXPlugin() {
ALOGI("createOMXPlugin");
return new SoftOMXPlugin();
}
SoftOMXPlugin类声明:【省略父类其他代码】
其声明可知,其实它没有新增任何自定义类成员,都是父类方法。
// [frameworks/av/media/libstagefright/omx/include/media/stagefrgiht/omx/SoftOMXPlugin.h]
struct SoftOMXPlugin : public OMXPluginBase {
SoftOMXPlugin();
virtual OMX_ERRORTYPE makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component);
virtual OMX_ERRORTYPE destroyComponentInstance(
OMX_COMPONENTTYPE *component);
virtual OMX_ERRORTYPE enumerateComponents(
OMX_STRING name,
size_t size,
OMX_U32 index);
virtual OMX_ERRORTYPE getRolesOfComponent(
const char *name,
Vector<String8> *roles);
private:
// 不允许该类对象赋值运算,也就是不允许一个变量赋值给另一个变量,只能自己创建。
// 此为宏定义,此前很多章节分析中已阐述过不再分析
DISALLOW_EVIL_CONSTRUCTORS(SoftOMXPlugin);
};
// [native/headers/media_plugin/media/hardware/OMXPluginBase.h]
struct OMXPluginBase {}
SoftOMXPlugin类构造函数实现:
实际是个空实现。其实该插件的具体都是在这几个方法中,并且也是通过加装所有的不同的软编解码器插件so库来实现的。
// [frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp]
SoftOMXPlugin::SoftOMXPlugin() {
}
1.3、mPlugins的声明实现:
将创建成功的插件对象指针缓存在mPlugins插件列表集合全局变量中
// [frameworks/av/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h]
struct OMXMaster : public OMXPluginBase {
private:
// 该结构声明:插件实现对象指针及其实现的so库。
struct Plugin {
OMXPluginBase *mOmx;
void *mLibHandle;
};
List<Plugin> mPlugins;
}
1.4、addPlugin(plugin)实现分析:
添加插件实现对象指针
// [frameworks/av/media/libstagefright/omx/OMXMaster.cpp]
void OMXMaster::addPlugin(OMXPluginBase *plugin) {
// 加锁访问
Mutex::Autolock autoLock(mLock);
// 32位对齐无符号整数
// typedef uint32_t OMX_U32;
// OMX_U32 is a 32 bit unsigned quantity that is 32 bit word aligned
OMX_U32 index = 0;
char name[128];
// OMX框架定义的错误码枚举类型,位于[native/headers/media_plugin/media/openmax/OMX_Core.h]
OMX_ERRORTYPE err;
// 枚举组件处理即循环读取OMX实现插件支持的所有音视频编解码器对应的组件名称
while ((err = plugin->enumerateComponents(
name, sizeof(name), index++)) == OMX_ErrorNone) {
String8 name8(name);
// KeyedVector<String8, OMXPluginBase *> mPluginByComponentName;
// 根据变量声明,mPluginByComponentName该变量其实相当于一个MAP映射对象,
// key为name值,value为具体的编解码器组件实现实例对象指针。
// 因此此处检查当前组件是否已加载过,加载过则返回其索引,大于等于0则直接跳过
if (mPluginByComponentName.indexOfKey(name8) >= 0) {
ALOGE("A component of name '%s' already exists, ignoring this one.",
name8.string());
continue;
}
// 添加到组件集合映射对象中
mPluginByComponentName.add(name8, plugin);
}
// 由上面实现分析,可知,当错误不是 OMX_ErrorNoMore 时表明发生了组件内部其他错误。
if (err != OMX_ErrorNoMore) {
ALOGE("OMX plugin failed w/ error 0x%08x after registering %zu "
"components", err, mPluginByComponentName.size());
}
}
plugin->enumerateComponents(name, sizeof(name), index++)实现分析:
需要每个具体插件的该方法实现
由1.2小节中分析可知,
先看下QComOMXPlugin的enumerateComponents() 方法实现:
其实可以看出,执行对应方法指针,高通插件实现libstagefrighthw.so库实际相当于一个代理实现即代理插件具体实现库libOmxCore.so的功能。
// [hardware/qcom/media/libstagefrighthw/QComOMXPlugin.cpp]
OMX_ERRORTYPE QComOMXPlugin::enumerateComponents(
OMX_STRING name,
size_t size,
OMX_U32 index) {
if (mLibHandle == NULL) {
return OMX_ErrorUndefined;
}
return (*mComponentNameEnum)(name, size, index);
}
再分析安卓原生软编解码器库即libstagefright_softomx_plugin.so的该方法实现:
通过加装所有的不同的软编解码器插件so库来实现的。
// [frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp]
OMX_ERRORTYPE SoftOMXPlugin::enumerateComponents(
OMX_STRING name,
size_t /* size */,
OMX_U32 index) {
// kNumComponents表示提供的软编解码器插件组件总数,其定义见下面分析
// 因此传入的参数不能大于等于组件总数,index也就是指定需要加载的so库在组件列表中的索引值。
if (index >= kNumComponents) {
// 无更多组件错误码
return OMX_ErrorNoMore;
}
// 然后将其组件name值复制给参数name返回
strcpy(name, kComponents[index].mName);
// 无错误码
return OMX_ErrorNone;
}
kNumComponents定义:
提供的软编解码器插件(组件)总数
// [frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp]
static const size_t kNumComponents =
sizeof(kComponents) / sizeof(kComponents[0]);
kComponents 定义所有提供的软编解码器插件(组件):
// [frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp]
static const struct {
const char *mName;
const char *mLibNameSuffix;
const char *mRole;
} kComponents[] = {
// two choices for aac decoding.
// configurable in media/libstagefright/data/media_codecs_google_audio.xml
// default implementation
{ "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
// alternate implementation
{ "OMX.google.xaac.decoder", "xaacdec", "audio_decoder.aac" },
{ "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
{ "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
{ "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
{ "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
{ "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
{ "OMX.google.h264.decoder", "avcdec", "video_decoder.avc" },
{ "OMX.google.h264.encoder", "avcenc", "video_encoder.avc" },
{ "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" },
{ "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
{ "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
{ "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" },
{ "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
{ "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
{ "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
{ "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },
{ "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
{ "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
{ "OMX.google.opus.decoder", "opusdec", "audio_decoder.opus" },
{ "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" },
{ "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" },
{ "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },
{ "OMX.google.vp9.encoder", "vpxenc", "video_encoder.vp9" },
{ "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },
{ "OMX.google.flac.decoder", "flacdec", "audio_decoder.flac" },
{ "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },
{ "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" },
#ifdef QTI_FLAC_DECODER
{ "OMX.qti.audio.decoder.flac", "qtiflacdec", "audio_decoder.flac" },
#endif
};
从这里可以知道,此处mName值其实对应前一章节分析的音视频编解码器配置文件中的MediaCodec节点的name值,mLibNameSuffix值表示每个软编解码器so库组件的后缀,role节点角色信息具体表示的信息如下定义。
注意:安卓原生自带的这些软编解码器组件其实都是以单个的so库形式存在的,如下真实pad中存在路径几乎都在vendor目录下,也需要注意解码器组件会比编码器组件多,其实就是安卓原生不需要支持不常见的编码格式,并且可能有些解码器组件so库存在于多个不同的目录中,也可能同一个编解码器格式由不同厂商实现,如上aac解码器有两个不同配置实现的so库。【高通硬解码器则不是单独so库存在形式】
NOTE:OMX.qti 开头的编解码器其实也是软编解码器,而非硬编解码器,qti其实应该表示的是高通子公司,也就是这个软编解码器应该是该公司实现的,这和一些旧版本出的书上有些差别了。
而它们的实现代码都在【frameworks/av/media/libstagefright/codecs/】目录下对应不同的格式目录实现的。
因此如上可以知道,高通的实现肯定也是类似实现啦。
2、MediaCodecsXmlParser类声明和构造函数:
MediaCodecsXmlParser类声明【省略其他代码】
Impl声明为其内部类
// [frameworks/av/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h]
class MediaCodecsXmlParser {
private:
struct Impl;
std::shared_ptr<Impl> mImpl;
}
MediaCodecsXmlParser类构造函数
其实MediaCodecsXmlParser的功能实现基本都是由Impl代理实现类来实现的。
MediaCodecsXmlParser类的功能实现职责正如类名描述,媒体编解码器xml文件信息解析,也就是前面章节分析的各种音视频编解码器xml配置文件信息的解析,也就是对xml中每个节点值进行解析,获取到每个编解码器的配置信息,此类功能不必详细分析,只需要知道其实现和提供的功能职责即可啦,感兴趣可以自行分析下,此处不再分析了。【以后有时间考虑分析下吧 TODO】
// [frameworks/av/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp]
MediaCodecsXmlParser::MediaCodecsXmlParser()
: mImpl(new Impl()) {
}
3、mParser.parseXmlFilesInSearchDirs()实现分析:
方法声明【参数为默认值】,其实这两个默认参数值在前面章节已详细分析过了,即默认的音视频编解码器xml配置信息文件,及其默认尝试搜索这些文件的文件夹路径。
此方法功能就是,对xml中每个节点值进行解析,获取到每个编解码器的配置信息。
// [frameworks/av/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h]
/**
* Parse top level XML files from a group of search directories.
*
* @param xmlFiles ordered list of XML file names (no paths)
* @param searchDirs ordered list of paths to consider
*
* @return parsing status
*/
status_t parseXmlFilesInSearchDirs(
const std::vector<std::string> &xmlFiles = getDefaultXmlNames(),
const std::vector<std::string> &searchDirs = getDefaultSearchDirs());
方法实现
正如第2小节所述,执行了具体代理实现类Impl的实现,它内部的实现不再分析。
// [frameworks/av/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp]
status_t MediaCodecsXmlParser::parseXmlFilesInSearchDirs(
const std::vector<std::string> &fileNames,
const std::vector<std::string> &searchDirs) {
return mImpl->parseXmlFilesInSearchDirs(fileNames, searchDirs);
}
本章内容分析结束。请查看其它章节后续分析。