OpenMAX确立了一套标准的接口,上层App直接调用这些接口,底层硬件厂商直接实现这些接口,
从而实现了上层软件开发与底层芯片开发地彻底分离,加速了跨平台的多媒体组件的开发、整合和编程。
Android上的MediaCodec是通过ACodec来加载openmax层,了解OMX加载过程,有助于我们更好的分析问题和解决问题
加载过程还需从ACodec::UninitializedState::onAllocateComponent接口说起。
bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) {
……
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
componentName = matchingCodecs[matchIndex];
OMXClient client;
bool trebleFlag;
if (client.connect(owners[matchIndex].c_str(), &trebleFlag) != OK) {
mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
return false;
}
omx = client.interface();
pid_t tid = gettid();
int prevPriority = androidGetThreadPriority(tid);
androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
androidSetThreadPriority(tid, prevPriority);
if (err == OK) {
mCodec->setTrebleFlag(trebleFlag);
ALOGE("codec %s selected", componentName.c_str());
break;
} else {
ALOGE("Allocating component '%s' failed, try next one.", componentName.c_str());
}
omxNode = NULL;
}
可以简单理解为两步
1、获取OMX句柄
2、分配component
我们可以将此过程理解为 C/S模型,客户端/服务器模型
运行在mediaserver或者app进程内的 MediaCodec是 client
运行在media.codec进程内的component是 server
MediaCodec通过 OMXClient,经过binder进程间通讯, 向MediaCodecService获取 OMX句柄
如下:
OMXClient client;
bool trebleFlag;
if (client.connect(owners[matchIndex].c_str(), &trebleFlag) != OK) {
mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
return false;
}
omx = client.interface();
OMXClient 通过connect接口与 MediaCodecService建立通讯,如下:
status_t OMXClient::connect(const char* name, bool* trebleFlag) {
if (property_get_bool("persist.media.treble_omx", true)) {
if (trebleFlag != nullptr) {
*trebleFlag = true;
}
return connectTreble(name);
}
if (trebleFlag != nullptr) {
*trebleFlag = false;
}
return connectLegacy();
}
status_t OMXClient::connectLegacy() {
sp sm = defaultServiceManager();
sp codecbinder = sm->getService(String16("media.codec"));
sp codecservice = interface_cast(codecbinder);
if (codecservice.get() == NULL) {
ALOGE("Cannot obtain IMediaCodecService");
return NO_INIT;
}
mOMX = codecservice->getOMX();
if (mOMX.get() == NULL) {
ALOGE("Cannot obtain mediacodec IOMX");
return NO_INIT;
}
return OK;
}
status_t OMXClient::connectTreble(const char* name) {
using namespace ::android::hardware::media::omx::V1_0;
if (name == nullptr) {
name = "default";
}
sp tOmx = IOmx::getService(name);
if (tOmx.get() == nullptr) {
ALOGE("Cannot obtain Treble IOmx.");
return NO_INIT;
}
if (!tOmx->isRemote()) {
ALOGE("Treble IOmx is in passthrough mode.");
return NO_INIT;
}
mOMX = new utils::LWOmx(tOmx);
ALOGI("Treble IOmx obtained");
return OK;
}
自Android 8.0起,google引入了 Project Treble,google的原生代码(FrameWork框架部分)编译至system.img镜像,华为、三星等手机制造厂商的vendor定制化代码和feather(HAL驱动模块)编译至vendor.img镜像,可以解耦google开发与手机厂商升级工作,以便实现更快地迭代,而system和vendor镜像之间的通讯是通过一种hidl语言来实现的,本质上仍然是IPC binder的一种,所以8.0上,OMXClinet获取 OMX 接口的方式有两种:
1、从MediaCodecService获取 (media.codec 进程)
2、从 Treble获取 (hw/[email protected] 进程)
本质都是通过binder获取server提供的句柄和接口。
以下我们仍以传统的非Treble的 connectLegacy 方式, 即从MediaCoderService中获取OMX的句柄 mOMX。
我们进一步看下 codecservice->getOMX() 做了什么:
sp MediaCodecService::getOMX() {
Mutex::Autolock autoLock(mOMXLock);
if (mOMX.get() == NULL) {
mOMX = new OMX();
}
return mOMX;
}
继续查看OMX类的构造函数
OMX::OMX() : mMaster(new OMXMaster), mParser() {
}
两个作用,
1、创建 OMXMaster
2、创建解析xml文件的parser
进一步查看 OMXMaster的构造函数
OMXMaster::OMXMaster()
: mVendorLibHandle(NULL) {
pid_t pid = getpid();
char filename[20];
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, "", sizeof(mProcessName));
} else {
ssize_t len = read(fd, mProcessName, sizeof(mProcessName));
if (len < 2) {
ALOGW("couldn't determine process name");
strlcpy(mProcessName, "", sizeof(mProcessName));
} else {
// the name is newline terminated, so erase the newline
mProcessName[len - 1] = 0;
}
close(fd);
}
addVendorPlugin();
addPlugin(new SoftOMXPlugin);
}
加载vendor和google的component。
addVendorPlugin();
……
void OMXMaster::addVendorPlugin() {
addPlugin("libstagefrighthw.so");
}
void OMXMaster::addPlugin(const char *libname) {
mVendorLibHandle = dlopen(libname, RTLD_NOW);
if (mVendorLibHandle == NULL) {
return;
}
typedef OMXPluginBase *(*CreateOMXPluginFunc)();
CreateOMXPluginFunc createOMXPlugin =
(CreateOMXPluginFunc)dlsym(
mVendorLibHandle, "createOMXPlugin");
if (!createOMXPlugin)
createOMXPlugin = (CreateOMXPluginFunc)dlsym(
mVendorLibHandle, "_ZN7android15createOMXPluginEv");
if (createOMXPlugin) {
addPlugin((*createOMXPlugin)());
}
}
加载 libstagefrighthw.so,并获取 createOMXPlugin 函数地址,之后 addPlugin((*createOMXPlugin)());
类似加载google的component, addPlugin(new SoftOMXPlugin);
参考google的 SoftOMXPlugin类,我们在libstagefrighthw.so中至少实现
makeComponentInstance, destroyComponentInstance,enumerateComponents 和 getRolesOfComponent这些接口
具体接口见
addPlugin(new SoftOMXPlugin);
……
void OMXMaster::addPlugin(OMXPluginBase *plugin) {
Mutex::Autolock autoLock(mLock);
mPlugins.push_back(plugin);
OMX_U32 index = 0;
char name[128];
OMX_ERRORTYPE err;
while ((err = plugin->enumerateComponents(
name, sizeof(name), index++)) == OMX_ErrorNone) {
String8 name8(name);
if (mPluginByComponentName.indexOfKey(name8) >= 0) {
ALOGE("A component of name '%s' already exists, ignoring this one.",
name8.string());
continue;
}
mPluginByComponentName.add(name8, plugin);
}
if (err != OMX_ErrorNoMore) {
ALOGE("OMX plugin failed w/ error 0x%08x after registering %zu "
"components", err, mPluginByComponentName.size());
}
}
枚举所有的 component并和plugin放置于 mPluginByComponentName
至此完成了vendor和google所有的component的加载。
关系如下:
omx = client.interface();
至此 OMXClient通过mOMX 与 MediaCodecService建立了联系,并返回mOMX给 ACodec
err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
从上文可知,omx为MediaCodecService返回来的句柄,我们直接查看MediaCodecService中OMX类的方法
status_t OMX::allocateNode(
const char *name, const sp &observer,
sp *omxNode) {
Mutex::Autolock autoLock(mLock);
omxNode->clear();
if (mLiveNodes.size() == kMaxNodeInstances) {
return NO_MEMORY;
}
sp instance = new OMXNodeInstance(this, observer, name);
OMX_COMPONENTTYPE *handle;
OMX_ERRORTYPE err = mMaster->makeComponentInstance(
name, &OMXNodeInstance::kCallbacks,
instance.get(), &handle);
if (err != OMX_ErrorNone) {
ALOGE("FAILED to allocate omx component '%s' err=%s(%#x)", name, asString(err), err);
return StatusFromOMXError(err);
}
instance->setHandle(handle);
// Find quirks from mParser
const auto& codec = mParser.getCodecMap().find(name);
if (codec == mParser.getCodecMap().cend()) {
ALOGW("Failed to obtain quirks for omx component '%s' from XML files",
name);
} else {
uint32_t quirks = 0;
for (const auto& quirk : codec->second.quirkSet) {
if (quirk == "requires-allocate-on-input-ports") {
quirks |= OMXNodeInstance::
kRequiresAllocateBufferOnInputPorts;
}
if (quirk == "requires-allocate-on-output-ports") {
quirks |= OMXNodeInstance::
kRequiresAllocateBufferOnOutputPorts;
}
}
instance->setQuirks(quirks);
}
mLiveNodes.add(IInterface::asBinder(observer), instance);
IInterface::asBinder(observer)->linkToDeath(this);
*omxNode = instance;
return OK;
}
主要工作有二:
1、创建OMXNodeInstance
2、分配真正的component,并返回句柄
未完……