OMX Service Codec加载
看一个总体的框架图
图来自 https://blog.csdn.net/VNanyesheshou/article/details/115027180
我这看的平台是Android 9
av/services/mediacodec/main_codecservice.cpp
// Registration of customized codec services
void *registrantLib = dlopen(
"libmedia_codecserviceregistrant.so",
RTLD_NOW | RTLD_LOCAL);
if (registrantLib) {
RegisterCodecServicesFunc registerCodecServices =
reinterpret_cast(
dlsym(registrantLib, "RegisterCodecServices"));
if (registerCodecServices) {
registerCodecServices();
} else {
LOG(WARNING) << "Cannot register additional services "
"-- corrupted library.";
}
} else {
// Default codec services
using namespace ::android::hardware::media::omx::V1_0;
sp omxStore = new implementation::OmxStore();
if (omxStore == nullptr) {
LOG(ERROR) << "Cannot create IOmxStore HAL service.";
} else if (omxStore->registerAsService() != OK) {
LOG(ERROR) << "Cannot register IOmxStore HAL service.";
}
sp omx = new implementation::Omx();
if (omx == nullptr) {
LOG(ERROR) << "Cannot create IOmx HAL service.";
} else if (omx->registerAsService() != OK) {
LOG(ERROR) << "Cannot register IOmx HAL service.";
} else {
LOG(INFO) << "IOmx HAL service created.";
}
}
我们这里主要看默认的case, 创建OmxStore, Omx, 分别注册服务
OmxStore 初始化解析
sp omxStore = new implementation::OmxStore();
上述omxStore调用了默认的构造函数,看到 cpp中的实现都是带参数的
frameworks/av/media/libstagefright/omx/1.0/OmxStore.cpp
OmxStore::OmxStore(
const char* owner,
const char* const* searchDirs,
const char* mainXmlName,
const char* performanceXmlName,
const char* profilingResultsXmlPath) {
MediaCodecsXmlParser parser(searchDirs,
mainXmlName,
performanceXmlName,
profilingResultsXmlPath);
mParsingStatus = toStatus(parser.getParsingStatus());
const auto& serviceAttributeMap = parser.getServiceAttributeMap();
mServiceAttributeList.resize(serviceAttributeMap.size());
size_t i = 0;
for (const auto& attributePair : serviceAttributeMap) {
ServiceAttribute attribute;
attribute.key = attributePair.first;
attribute.value = attributePair.second;
mServiceAttributeList[i] = std::move(attribute);
++i;
}
const auto& roleMap = parser.getRoleMap();
mRoleList.resize(roleMap.size());
i = 0;
for (const auto& rolePair : roleMap) {
RoleInfo role;
role.role = rolePair.first;
role.type = rolePair.second.type;
role.isEncoder = rolePair.second.isEncoder;
// TODO: Currently, preferPlatformNodes information is not available in
// the xml file. Once we have a way to provide this information, it
// should be parsed properly.
role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
hidl_vec& nodeList = role.nodes;
nodeList.resize(rolePair.second.nodeList.size());
size_t j = 0;
for (const auto& nodePair : rolePair.second.nodeList) {
NodeInfo node;
node.name = nodePair.second.name;
node.owner = owner;
hidl_vec& attributeList = node.attributes;
attributeList.resize(nodePair.second.attributeList.size());
size_t k = 0;
for (const auto& attributePair : nodePair.second.attributeList) {
NodeAttribute attribute;
attribute.key = attributePair.first;
attribute.value = attributePair.second;
attributeList[k] = std::move(attribute);
++k;
}
nodeList[j] = std::move(node);
++j;
}
mRoleList[i] = std::move(role);
++i;
}
mPrefix = parser.getCommonPrefix();
}
于是去.h 中看看默认参数是什么,这里指定了默认参数在 MediaCodecsXmlParser 中定义
struct OmxStore : public IOmxStore {
OmxStore(
const char* owner = "default",
const char* const* searchDirs
= MediaCodecsXmlParser::defaultSearchDirs,
const char* mainXmlName
= MediaCodecsXmlParser::defaultMainXmlName,
const char* performanceXmlName
= MediaCodecsXmlParser::defaultPerformanceXmlName,
const char* profilingResultsXmlPath
= MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
// ...
}
在 MediaCodecsXmlParser.h 路劲中可以看到默认路径定义如下
// Treblized media codec list will be located in /odm/etc or /vendor/etc.
static constexpr char const* defaultSearchDirs[] =
{"/odm/etc", "/vendor/etc", "/etc", nullptr};
static constexpr char const* defaultMainXmlName =
"media_codecs.xml";
static constexpr char const* defaultPerformanceXmlName =
"media_codecs_performance.xml";
static constexpr char const* defaultProfilingResultsXmlPath =
"/data/misc/media/media_codecs_profiling_results.xml";
具体再去跟踪 cpp中的构造函数,可以看到 OMXStore初始化通过MediaCodecsXmlParser加载media_codecs.xml、media_codecs_performance.xml,并将信息保存在mServiceAttributeList、mRoleList中
OMX 初始化解析
初始化完OmxStore后,接下来就初始化 OMX的主要服务了
Omx::Omx() :
mMaster(new OMXMaster()),
mParser() {
}
OMXMaster 加载和管理 plugin,包括硬编码和软编码,这里主要是 addVendorPlugin 和 addPlugin(new SoftOMXPlugin); 这两个函数的实现
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 Plugin
addVendorPlugin加载共享库libstagefrighthw.so,查找createOMXPlugin函数,并调用createOMXPlugin 获得OMXPluginBase类型对象,然后调用 addPlugin((*createOMXPlugin)());
以Mstar/Mtk 为例,libstagefrighthw 的实现是在 MS_OMX_Plugin.cpp这个文件中
Android.mk
LOCAL_MODULE := libstagefrighthw
LOCAL_SRC_FILES := MS_OMX_Plugin.cpp
MS_OMX_Plugin.cpp
OMXPluginBase *createOMXPlugin() {
return new MSOMXPlugin;
}
而 OMXPluginBase 是有Android 定义的,用来对接hal vendor接口的实现, 其主要包含四个虚拟方法, 硬件厂商接入自己的编解码器,需要继承OMXPluginBase 类,并实现抽象方法。
frameworks/native/headers/media_plugin/media/hardware/OMXPluginBase.h
struct OMXPluginBase {
OMXPluginBase() {}
virtual ~OMXPluginBase() {}
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;
virtual OMX_ERRORTYPE enumerateComponents(
OMX_STRING name,
size_t size,
OMX_U32 index) = 0;
virtual OMX_ERRORTYPE getRolesOfComponent(
const char *name,
Vector *roles) = 0;
private:
OMXPluginBase(const OMXPluginBase &);
OMXPluginBase &operator=(const OMXPluginBase &);
};
更多vendor的细节可以去 MS_OMX_Plugin.cpp 里面寻找
Software Plugin
Software Plugin 定义是在 SoftOMXPlugin.cpp,其接口和定义同样遵循 OMXPluginBase 结构的要求,同样需要实现上述的OMXPluginBase.h 抽象方法
struct SoftOMXPlugin : public OMXPluginBase {
}
而 Software Plugin 定义的组件支持在这里
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" },
};
这里的例子是加载了两个插件,当然也可以根据需要再添加更多的插件进来,而每个插件中还有组件的定义,比如说 SoftOMXPlugin 这个插件里面 有支持OMX.google.h264.decoder 的组件和 OMX.google.hevc.decoder,在这里面也可以添加想要支持的组件
回到 *addPlugin(OMXPluginBase plugin) 方法,可以先看下 OMXMaster.h中定义的数据结构
List mPlugins;
KeyedVector mPluginByComponentName;
KeyedVector mPluginByInstance;
结合如下函数方法可以看到 mPlugins 维护了目前系统之有多少组插件(Vendor, Software ....), 而 mPluginByComponentName 维护了每个插件中支持的组件能力
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());
}
}
而 mPluginByInstance 这个数据结构则要看如下 makeComponentInstance 这个函数的实现,这个函数是实例化组件中的插件,比如说要初始化 OMX.google.h264.decoder这个组件,就会调用 makeComponentInstance(),而如果实例化成功了,即 err = OMX_ErrorNone 就会添加进来到 mPluginByInstance。
OMX_ERRORTYPE OMXMaster::makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component) {
ALOGI("makeComponentInstance(%s) in %s process", name, mProcessName);
Mutex::Autolock autoLock(mLock);
*component = NULL;
ssize_t index = mPluginByComponentName.indexOfKey(String8(name));
if (index < 0) {
return OMX_ErrorInvalidComponentName;
}
OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
OMX_ERRORTYPE err =
plugin->makeComponentInstance(name, callbacks, appData, component);
if (err != OMX_ErrorNone) {
return err;
}
mPluginByInstance.add(*component, plugin);
return err;
}
那么什么时候会出现 makeComponentInstance 失败了呢?一个常见的例子是hw codec 如果没有hashkey lisence 就会初始初始化,可以看到目前平台简单的流程如下
makeComponentInstance --> mGetHandle --> MS_OMX_GetHandle --> OMX_GetHandle -- > check from MS_OMX_ScanSupportedCodec
01-01 00:00:01.652 3321 3499 I OMXClient: IOmx service obtained
01-01 00:00:01.653 3336 3921 I OMXMaster: makeComponentInstance(OMX.MS.WMA.Decoder) in [email protected] process
01-01 00:00:01.653 3336 3921 E media.codec: Failed to allocate omx component 'OMX.MS.WMA.Decoder' err=InvalidComponentName(0x80001002)
01-01 00:00:01.653 3321 3499 W OmxInfoBuilder: Fail to add mime audio/x-ms-wma to codec OMX.MS.WMA.Decoder after software codecs
那么什么时候会调取用到 makeComponentInstance ?可以接着往下看
MediaCodecList初始化
前面介绍了一些 OMX的初始化,包括解析media_codec.xml 数据,那么什么时候会使用到解析的数据并如何更新到系统中
MediaCodecList::MediaCodecList(std::vector builders) {
mInitCheck = builder->buildMediaCodecList(&writer);
}
frameworks/av/media/libstagefright/OmxInfoBuilder.cpp 先去获取前面OmxStore 解析的media_codec.xml中支持的codec 列表,即 roles 和 serviceAttributes 数据
status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
// List service attributes (global settings)
Status status;
hidl_vec roles;
auto transStatus = omxStore->listRoles(
[&roles] (
const hidl_vec& inRoleList) {
roles = inRoleList;
});
hidl_vec serviceAttributes;
transStatus = omxStore->listServiceAttributes(
[&status, &serviceAttributes] (
Status inStatus,
const hidl_vec& inAttributes) {
status = inStatus;
serviceAttributes = inAttributes;
});
// 接下来遍历 xml 中的节点数据
for (const IOmxStore::RoleInfo& role : roles) {
const hidl_string& typeName = role.type;
bool isEncoder = role.isEncoder; // xml中有有声明encoder和decoder 的支持组件
bool preferPlatformNodes = role.preferPlatformNodes;
/* 这个属性注释如下,如果优先平台的组件需要添加到软解组件下面,下面的判断用回到 preferPlatformNodes */
// If preferPlatformNodes is true, hardware nodes must be added after
// platform (software) nodes. hwCodecs is used to hold hardware nodes
// that need to be added after software nodes for the same role.
std::vector hwCodecs;
for (const IOmxStore::NodeInfo& node : role.nodes) {
const hidl_string& nodeName = node.name;
bool isSoftware = hasPrefix(nodeName, "OMX.google");
MediaCodecInfoWriter* info;
// 对软解组件和硬解组件分别归类到 swCodecName2Info、hwCodecName2Info
}
// If preferPlatformNodes is true, hardware nodes will not have been
// added in the loop above, but rather saved in hwCodecs. They are
// going to be added here.
if (preferPlatformNodes) {
for (const IOmxStore::NodeInfo *node : hwCodecs) {
MediaCodecInfoWriter* info;
const hidl_string& nodeName = node->name;
auto c2i = hwCodecName2Info.find(nodeName);
std::unique_ptr caps =
info->addMime(typeName.c_str());
if (queryCapabilities(
*node, typeName.c_str(), isEncoder, caps.get()) != OK) {
ALOGW("Fail to add mime %s to codec %s "
"after software codecs",
typeName.c_str(), nodeName.c_str());
info->removeMime(typeName.c_str());
}
}
}
}
}
上面的 buildMediaCodecList 这个函数挺长的,过了一遍下来后其主要是获取media_codec.xml 中解析的节点组件数据,然后还有一个重要的作用就是将硬件的组件的支持能力更新到MediaCodecInfo::mCaps 中
简单介绍下这个数据接口的作用 ,方便理解里面的逻辑
MediaCodecInfo:: KeyedVector > mCaps;
这是一个Vector 数组,不过带有支持的组件名,举个例子
mCaps[2] = {
<"OMX.MS.AC3.Decoder", Capabilities(audio/ac3)>,
<"OMX.MS.AC3.Decoder", Capabilities(audio/ac3p)>
}
通过 info->addMime 添加 audio/ac3p 到 OMX.MS.AC3.Decoder组件
然后 queryCapabilities 调用 makeComponentInstance等流程检查平台是否支持
如果不支持则 info->removeMime 移除OMX.MS.AC3.Decoder 对 audio/ac3p的支持
而这个mCaps 的MediaCodecInfo会进一步提供给 MediaCodecList::findCodecByType 和 MediaCodecList::findMatchingCodecs 使用,然后进一步封装后给player在初始化decoder的时候用于查找目前系统支持的codec 组件
看MediaCodecList 这块过程中打开了一些打印调试和理清
diff --git a/frameworks/av/media/libmedia/MediaCodecInfo.cpp b/frameworks/av/media/libmedia/MediaCodecInfo.cpp
index 26588c0..c74c39d 100644
--- a/frameworks/av/media/libmedia/MediaCodecInfo.cpp
+++ b/frameworks/av/media/libmedia/MediaCodecInfo.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
#define LOG_TAG "MediaCodecInfo"
#include
@@ -192,7 +192,9 @@ void MediaCodecInfo::getSupportedMimes(Vector *mimes) const {
const sp
MediaCodecInfo::getCapabilitiesFor(const char *mime) const {
+ ALOGD("%s Enter", __FUNCTION__);
ssize_t ix = getCapabilityIndex(mime);
+ ALOGD("%s ix:%d", __FUNCTION__, ix);
if (ix >= 0) {
return mCaps.valueAt(ix);
}
@@ -225,6 +227,8 @@ sp MediaCodecInfo::FromParcel(const Parcel &parcel) {
if (caps == NULL)
return NULL;
if (info != NULL) {
+ ALOGD("%s %d codec:%s mime:%s",
+ __FUNCTION__, __LINE__, info->mName.c_str(), mime.c_str());
info->mCaps.add(mime, caps);
}
}
@@ -246,7 +250,9 @@ status_t MediaCodecInfo::writeToParcel(Parcel *parcel) const {
ssize_t MediaCodecInfo::getCapabilityIndex(const char *mime) const {
if (mime) {
+ ALOGD("%s mCaps.size:%d", __FUNCTION__, mCaps.size());
for (size_t ix = 0; ix < mCaps.size(); ix++) {
+ ALOGD("search mime:%s in [%d] %s", mime, ix, mCaps.keyAt(ix).c_str());
if (mCaps.keyAt(ix).equalsIgnoreCase(mime)) {
return ix;
}
@@ -284,6 +290,8 @@ std::unique_ptr
}
sp caps = new MediaCodecInfo::Capabilities();
mInfo->mCaps.add(AString(mime), caps);
+ ALOGD("%s %d codec:%s mime:%s",
+ __FUNCTION__, __LINE__, mInfo->mName.c_str(), mime);
return std::unique_ptr(
new MediaCodecInfo::CapabilitiesWriter(caps.get()));
}
@@ -291,6 +299,8 @@ std::unique_ptr
bool MediaCodecInfoWriter::removeMime(const char *mime) {
ssize_t ix = mInfo->getCapabilityIndex(mime);
if (ix >= 0) {
+ ALOGD("%s %d codec:%s mime:%s",
+ __FUNCTION__, __LINE__, mInfo->mName.c_str(), mime);
mInfo->mCaps.removeItemsAt(ix);
return true;
}
diff --git a/frameworks/av/media/libstagefright/MediaCodecList.cpp b/frameworks/av/media/libstagefright/MediaCodecList.cpp
index eaff283..ffe3083 100644
--- a/frameworks/av/media/libstagefright/MediaCodecList.cpp
+++ b/frameworks/av/media/libstagefright/MediaCodecList.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
#define LOG_TAG "MediaCodecList"
#include
@@ -243,8 +243,10 @@ ssize_t MediaCodecList::findCodecByType(
size_t numCodecInfos = mCodecInfos.size();
for (; startIndex < numCodecInfos; ++startIndex) {
const MediaCodecInfo &info = *mCodecInfos[startIndex];
-
+ ALOGD("%s %s", __FUNCTION__, info.getCodecName());
if (info.isEncoder() != encoder) {
+ ALOGD("%s isEncoder:%d == encoder:%d",
+ __FUNCTION__, info.isEncoder(), encoder);
continue;
}
sp capabilities = info.getCapabilitiesFor(type);
调试文件的文件在 log\mediacodeclist.log