Android 12(S) MultiMedia(十二)MediaCodecList & IOmxStore
这节来了解下MediaCodecList相关代码路径:
frameworks/av/media/libstagefright/MediaCodecList.cpp
frameworks/av/media/libstagefright/OmxInfoBuilder.cpp
frameworks/av/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
参考MediaCodec中的使用方法:
const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
sp<IMediaCodecList> MediaCodecList::getInstance() {
Mutex::Autolock _l(sRemoteInitMutex);
if (sRemoteList == nullptr) {
sMediaPlayer = defaultServiceManager()->getService(String16("media.player"));
sp<IMediaPlayerService> service =
interface_cast<IMediaPlayerService>(sMediaPlayer);
if (service.get() != nullptr) {
sRemoteList = service->getCodecList();
if (sRemoteList != nullptr) {
sBinderDeathObserver = new BinderDeathObserver();
sMediaPlayer->linkToDeath(sBinderDeathObserver.get());
}
}
if (sRemoteList == nullptr) {
// if failed to get remote list, create local list
sRemoteList = getLocalInstance();
}
}
return sRemoteList;
}
MediaCodecList使用的是单例模式,MediaCodecList有它对应的Bp和Bn,原先以为是一个binder service,其实并不是,这样定义只是方便了使用mediaplayerservice来获取一个MediaCodecList对象。
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
Mutex::Autolock autoLock(sInitMutex);
if (sCodecList == nullptr) {
MediaCodecList *codecList = new MediaCodecList(GetBuilders());
if (codecList->initCheck() == OK) {
sCodecList = codecList;
if (isProfilingNeeded()) {
ALOGV("Codec profiling needed, will be run in separated thread.");
pthread_t profiler;
if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
ALOGW("Failed to create thread for codec profiling.");
}
}
} else {
// failure to initialize may be temporary. retry on next call.
delete codecList;
}
}
return sCodecList;
}
getLocalInstance获取创建一个MediaCOdecList实例,它有一个参数GetBuilders():
std::vector<MediaCodecListBuilderBase *> GetBuilders() {
std::vector<MediaCodecListBuilderBase *> builders;
// if plugin provides the input surface, we cannot use OMX video encoders.
// In this case, rely on plugin to provide list of OMX codecs that are usable.
sp<PersistentSurface> surfaceTest = CCodec::CreateInputSurface();
if (surfaceTest == nullptr) {
ALOGD("Allowing all OMX codecs");
builders.push_back(&sOmxInfoBuilder);
} else {
ALOGD("Allowing only non-surface-encoder OMX codecs");
builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder);
}
builders.push_back(GetCodec2InfoBuilder());
return builders;
}
GetBuilders方法返回一个MediaCodecListBuilderBase指针数组,数组中有sOmxInfoBuilder和Codec2InfoBuilder两个成员
OmxInfoBuilder sOmxInfoBuilder{true /* allowSurfaceEncoders */};
MediaCodecListBuilderBase *GetCodec2InfoBuilder() {
Mutex::Autolock _l(sCodec2InfoBuilderMutex);
if (!sCodec2InfoBuilder) {
sCodec2InfoBuilder.reset(new Codec2InfoBuilder);
}
return sCodec2InfoBuilder.get();
}
接下来就正式看看MediaCodecList的构造函数做了什么事情。
MediaCodecListWriter writer;
for (MediaCodecListBuilderBase *builder : builders) {
if (builder == nullptr) {
ALOGD("ignored a null builder");
continue;
}
auto currentCheck = builder->buildMediaCodecList(&writer);
if (currentCheck != OK) {
ALOGD("ignored failed builder");
continue;
} else {
mInitCheck = currentCheck;
}
}
构造函数遍历了入参数组,并调用了他们的buildMediaCodecList方法,我们先看sOmxInfoBuilder:
sOmxInfoBuilder
它主要加载的是厂商提供的硬解组件信息
status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
// 1、Obtain IOmxStore
sp<IOmxStore> omxStore = IOmxStore::getService();
Status status;
hidl_vec<IOmxStore::RoleInfo> roles;
// 2、listRoles
auto transStatus = omxStore->listRoles(
[&roles] (
const hidl_vec<IOmxStore::RoleInfo>& inRoleList) {
roles = inRoleList;
});
// 3、listServiceAttributes
hidl_vec<IOmxStore::ServiceAttribute> serviceAttributes;
transStatus = omxStore->listServiceAttributes(
[&status, &serviceAttributes] (
Status inStatus,
const hidl_vec<IOmxStore::ServiceAttribute>& inAttributes) {
status = inStatus;
serviceAttributes = inAttributes;
});
// 4、addGlobalSetting
for (const auto& p : serviceAttributes) {
writer->addGlobalSetting(
p.key.c_str(), p.value.c_str());
}
std::map<hidl_string, std::unique_ptr<MediaCodecInfoWriter>> codecName2Info;
uint32_t defaultRank =
::android::base::GetUintProperty("debug.stagefright.omx_default_rank", 0x100u);
uint32_t defaultSwAudioRank =
::android::base::GetUintProperty("debug.stagefright.omx_default_rank.sw-audio", 0x10u);
uint32_t defaultSwOtherRank =
::android::base::GetUintProperty("debug.stagefright.omx_default_rank.sw-other", 0x210u);
// 5、将roles转化为list
for (const IOmxStore::RoleInfo& role : roles) {
const hidl_string& typeName = role.type;
bool isEncoder = role.isEncoder;
bool isAudio = hasPrefix(role.type, "audio/");
bool isVideoOrImage = hasPrefix(role.type, "video/") || hasPrefix(role.type, "image/");
for (const IOmxStore::NodeInfo &node : role.nodes) {
const hidl_string& nodeName = node.name;
if (!mAllowSurfaceEncoders && isVideoOrImage && isEncoder) {
ALOGD("disabling %s for media type %s because we are not using OMX input surface",
nodeName.c_str(), role.type.c_str());
continue;
}
bool isSoftware = hasPrefix(nodeName, "OMX.google");
uint32_t rank = isSoftware
? (isAudio ? defaultSwAudioRank : defaultSwOtherRank)
: defaultRank;
for (const IOmxStore::Attribute& attribute : node.attributes) {
if (attribute.key == "rank") {
uint32_t oldRank = rank;
char dummy;
if (sscanf(attribute.value.c_str(), "%u%c", &rank, &dummy) != 1) {
rank = oldRank;
}
break;
}
}
MediaCodecInfoWriter* info;
auto c2i = codecName2Info.find(nodeName);
if (c2i == codecName2Info.end()) {
c2i = codecName2Info.insert(std::make_pair(
nodeName, writer->addMediaCodecInfo())).first;
info = c2i->second.get();
info->setName(nodeName.c_str());
info->setOwner(node.owner.c_str());
info->setRank(rank);
typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
if (!isSoftware) {
attrs |= MediaCodecInfo::kFlagIsVendor;
if (!std::count_if(
node.attributes.begin(), node.attributes.end(),
[](const IOmxStore::Attribute &i) -> bool {
return i.key == "attribute::software-codec";
})) {
attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
}
}
if (isEncoder) {
attrs |= MediaCodecInfo::kFlagIsEncoder;
}
info->setAttributes(attrs);
} else {
info = c2i->second.get();
}
std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
info->addMediaType(typeName.c_str());
if (queryCapabilities(
node, typeName.c_str(), isEncoder, caps.get()) != OK) {
ALOGW("Fail to add media type %s to codec %s",
typeName.c_str(), nodeName.c_str());
info->removeMediaType(typeName.c_str());
}
}
}
return OK;
}
OmxInfoBuilder::buildMediaCodecList大致可分为4步:
1、获取IOmxStore
2、调用IOmxStore的listRoles方法
3、调用IOmxStore的listServiceAttributes
4、addGlobalSetting
5、将listRoles返回结果转化为MediaCodecInfo
IOmxStore
IOmxStore是一个HIDL service,它和IOmx Service是在同一个地方注册的,参考frameworks/av/services/mediacodec/main_codecservice.cpp,
sp<IOmxStore> omxStore = new implementation::OmxStore(
property_get_int64("vendor.media.omx", 1) ? omx : nullptr);
if (omxStore == nullptr) {
LOG(ERROR) << "Cannot create IOmxStore HAL service.";
} else if (omxStore->registerAsService() != OK) {
LOG(ERROR) << "Cannot register IOmxStore HAL service.";
}
如果去看Omx.cpp的构造函数就可以发现,Omx service中持有一个OMXStore成员,注意这个OMXStore和我们这边的OmxStore是完全不同的两个东西!OMXStore加载了厂商提供的硬解组件。
我们这里用的OmxStore是加载了系统中可用的codec组件信息,接下来就看看OmxStore的构造函数做了什么?创建OmxStore时只传了一个参数,其实他还有5个默认参数
OmxStore(
const sp<IOmx> &omx = nullptr,
const char* owner = "default",
const std::vector<std::string> &searchDirs =
MediaCodecsXmlParser::getDefaultSearchDirs(),
const std::vector<std::string> &xmlFiles =
MediaCodecsXmlParser::getDefaultXmlNames(),
const char *xmlProfilingResultsPath =
MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
默认参数和MediaCodecsXmlParser公用的,看看都是些什么值
static std::vector<std::string> getDefaultSearchDirs() {
return { "/product/etc",
"/odm/etc",
"/vendor/etc",
"/system/etc" };
}
std::vector<std::string> MediaCodecsXmlParser::getDefaultXmlNames() {
static constexpr char const* prefixes[] = {
"media_codecs",
"media_codecs_performance"
};
static std::vector<std::string> variants = {
android::base::GetProperty("ro.media.xml_variant.codecs", ""),
android::base::GetProperty("ro.media.xml_variant.codecs_performance", "")
};
static std::vector<std::string> names = {
prefixes[0] + variants[0] + ".xml",
prefixes[1] + variants[1] + ".xml",
// shaping information is not currently variant specific.
"media_codecs_shaping.xml"
};
return names;
}
static constexpr char const* defaultProfilingResultsXmlPath = "/data/misc/media/media_codecs_profiling_results.xml";
意思就是从/product/etc /odm/etc /vendor/etc /system/etc下面去查找media_codecs.xml和media_codecs_performance.xml这两个文件并解析出来,最后保存到mRoleList当中。注意到构造NodeInfo时有一个owner,根据创建IOmxStore service的传值为default。
再回到OmxInfoBuilder中来,调用IOmxStore的listRoles和listServiceAttributes方法拿到roles和serviceAttributes之后(由于没有阅读parse xml的过程,所以暂不清楚他们的值是如何组织的)接下来需要将数据转化为MediaCodecInfo
MediaCodecInfoWriter* info;
auto c2i = codecName2Info.find(nodeName);
if (c2i == codecName2Info.end()) {
// Create a new MediaCodecInfo for a new node.
c2i = codecName2Info.insert(std::make_pair(
nodeName, writer->addMediaCodecInfo())).first;
info = c2i->second.get();
info->setName(nodeName.c_str());
info->setOwner(node.owner.c_str());
info->setRank(rank); typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
// all OMX codecs are vendor codecs (in the vendor partition), but
// treat OMX.google codecs as non-hardware-accelerated and non-vendor
if (!isSoftware) {
attrs |= MediaCodecInfo::kFlagIsVendor;
if (!std::count_if(
node.attributes.begin(), node.attributes.end(),
[](const IOmxStore::Attribute &i) -> bool {
return i.key == "attribute::software-codec";
})) {
attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
}
}
if (isEncoder) {
attrs |= MediaCodecInfo::kFlagIsEncoder;
}
info->setAttributes(attrs);
} else {
// The node has been seen before. Simply retrieve the
// existing MediaCodecInfoWriter.
info = c2i->second.get();
}
std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
info->addMediaType(typeName.c_str());
if (queryCapabilities(
node, typeName.c_str(), isEncoder, caps.get()) != OK) {
ALOGW("Fail to add media type %s to codec %s",
typeName.c_str(), nodeName.c_str());
info->removeMediaType(typeName.c_str());
}
Codec2InfoBuilder
它主要解析的是google提供的软解组件
parser.parseXmlFilesInSearchDirs(
{ "media_codecs.xml", "media_codecs_performance.xml" },
{ "/apex/com.android.media.swcodec/etc" });
解析的是 /apex/com.android.media.swcodec/etc/media_codecs.xml,例如:
<MediaCodec name="c2.android.vp9.decoder" type="video/x-vnd.on2.vp9" variant="slow-cpu,!slow-cpu">
<Alias name="OMX.google.vp9.decoder" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
<Variant name="!slow-cpu">
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="block-count" range="1-16384" />
<Limit name="blocks-per-second" range="1-500000" />
<Limit name="bitrate" range="1-40000000" />
</Variant>
<Variant name="slow-cpu">
<Limit name="size" min="2x2" max="1280x1280" />
<Limit name="block-count" range="1-3600" /> <!-- max 1280x720 -->
<Limit name="blocks-per-second" range="1-108000" />
<Limit name="bitrate" range="1-5000000" />
</Variant>
<Feature name="adaptive-playback" />
</MediaCodec>
其实看到这儿,也没看出个什么名堂!只知道了MediaCodecInfo中加载的是厂商的硬解组件信息(/vendor/etc/media_codecs.xml)和google软解组件信息(/apex/com.android.media.swcodec/etc/media_codecs.xml),加载的信息如何组织起来的也不清楚,其实通过dumpsys命令就可以看到支持的MediaCodecInfo了
$ dumpsys media.player
Media type 'video/x-vnd.on2.vp8':
Decoder "c2.android.vp8.decoder" supports
aliases: [
"OMX.google.vp8.decoder" ]
attributes: 0x4: [
encoder: 0,
vendor: 0,
software-only: 1,
hw-accelerated: 0 ]
owner: "codec2::software"
rank: 512
profile/levels: [
1/ 1 (Main/V0) ]
colors: [
0x7f420888 (YUV420Flexible),
0x13 (YUV420Planar),
0x15 (YUV420SemiPlanar),
0x14 (YUV420PackedPlanar),
0x27 (YUV420PackedSemiPlanar) ]
details: AMessage(what = 0x00000000) = {
string measured-frame-rate-1280x720-range = "29-100"
string measured-frame-rate-1920x1080-range = "11-44"
string measured-frame-rate-320x240-range = "250-300"
string measured-frame-rate-640x360-range = "130-300"
string alignment = "2x2"
string bitrate-range = "1-40000000"
string block-count-range = "1-8192"
string block-size = "16x16"
string blocks-per-second-range = "1-1000000"
int32_t feature-adaptive-playback = 0
string size-range = "2x2-2048x2048"
}
最后来看下MediaCodecList和相关类的关系,MediaPlayerService进程中有一个MediaCodecList全局变量,使用getCodecList获取的就是该全局变量。另外也可以通过直接调用MediaCodecList中的静态方法getLocalInstance或者getInstance方法来获取一个MediaCodecList对象,获取的也是MediaPlayerService进程中的全局变量。

Android 12(S) MultiMedia(十二)MediaCodecList & IOmxStore的更多相关文章
- Android图表库MPAndroidChart(十二)——来点不一样的,正负堆叠条形图
Android图表库MPAndroidChart(十二)--来点不一样的,正负堆叠条形图 接上篇,今天要说的,和上篇的类似,只是方向是有相反的两面,我们先看下效果 实际上这样就导致了我们的代码是比较类 ...
- Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View
Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...
- Android小项目之十二 设置中心的界面
------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...
- Gradle 1.12 翻译——第十二章 使用Gradle 图形用户界面
有关其他已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或访问:http://gradledoc.qiniudn.com ...
- Xamarin.Android开发实践(十二)
Xamarin.Android之ContentProvider 一.前言 掌握了如何使用SQLiteOpenHelper之后,我们就可以进行下一步的学习.本章我们将会学习如何使用ContentProv ...
- Android实战技巧之十二:Android Studio导入第三方类库、jar包和so库
第三方类库源码 将一网友的XMPP代码从ADT转到AS时,发现其使用了第三方类库,源码放在了lib下,直接在AS中Import project,第三方类库并没有自动导入进来,看来需要自己动手了. 项目 ...
- Android学习笔记(十二)——实战:制作一个聊天界面
//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 运用简单的布局知识,我们可以来尝试制作一个聊天界面. 一.制作 Nine-Patch 图片 : Nine-Pa ...
- android 项目学习随笔十二(ListView加脚布局)
1.ListView加脚布局 头布局initHeaderView,在onTouchEvent事件中进行显示隐藏头布局切换 脚布局initFooterView,实现接口OnScrollListener, ...
- Android学习笔记(十二)BroadcastReceiver的有序广播和优先级
前两篇博文中简单整理了普通广播,其实还有有序广播,有序广播在开发中也是比不可少的,可以给广播接收者设定优先级来控制接受顺序,并却可以中断广播传递等等. 一.两种Broadcast: · 普通广播(No ...
- Android OpenGL ES(十二):三维坐标系及坐标变换初步 .
OpenGL ES图形库最终的结果是在二维平面上显示3D物体(常称作模型Model)这是因为目前的打部分显示器还只能显示二维图形.但我们在构造3D模型时必须要有空间现象能力,所有对模型的描述还是使用三 ...
随机推荐
- 树模型-label boosting-GBDT
GBDT GBDT是boosting系列算法的代表之一,其核心是 梯度+提升+决策树. GBDT回归问题 通俗的理解: 先来个通俗理解:假如有个人30岁,我们首先用20岁去拟合,发现损失有10岁,这时 ...
- ionic 4 app 自动版本更新
前言 介绍一下ionic4 app的自动更新.ionic 不多介绍了,后面一个后系列,背负着骂名的ionic其实还是可以的,如果刚入门ionic可能觉得很坑,但是呢,往后你就发现另外一件事,那就是其他 ...
- 利用navicat实现excel转json
1.需要工具,Navicat Premium,网上有破解及安装教程 2.新建sqlite连接,选择新建sqlite3,如下图 3.接着点确定,如图 4. 5.
- Typescript 的数据类型有哪些?
一.是什么 typescript 和 javascript几乎一样,拥有相同的数据类型,另外在javascript基础上提供了更加实用的类型供开发使用 在开发阶段,可以为明确的变量定义为某种类型,这样 ...
- tensorflow如何切换CPU和GPU
import os if Bert_Use_GPU: os.environ['CUDA_VISIBLE_DEVICES'] = '0,1' #使用GPU0,1 else: os.environ['CU ...
- easyx的使用,鼠标交互(3.0)
本文从B站学习,借鉴: 学习视频地址:鼠标操作(旧版)_哔哩哔哩_bilibili
- 2024-04-21:用go语言,给一棵根为1的树,每次询问子树颜色种类数。 假设节点总数为n,颜色总数为m, 每个节点的颜色,依次给出,整棵树以1节点做头, 有k次查询,询问某个节点为头的子树,一共
2024-04-21:用go语言,给一棵根为1的树,每次询问子树颜色种类数. 假设节点总数为n,颜色总数为m, 每个节点的颜色,依次给出,整棵树以1节点做头, 有k次查询,询问某个节点为头的子树,一共 ...
- 力扣21(java&python)-合并两个有序链表(简单)
题目: 将两个升序链表合并为一个新的 升序 链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例 1: 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3 ...
- OpenYurt 深度解读:如何构建 Kubernetes 原生云边高效协同网络?
作者 | 郑超 导读:OpenYurt 是阿里巴巴开源的云边协同一体化架构,与同类开源方案相比,OpenYurt 拥有可实现边缘计算全场景覆盖的能力.在之前的一篇文章中,我们介绍了 OpenYurt ...
- WPF自定义控件的三种方式
简介: 某些场景下,我们确实需要创建新的控件.此时,理解 WPF不同控件的创建方法就显得非常重要. WPF 提供3个用于创建控件的方法,每个方法都提供不同的灵活度. WPF控件可以通过数据模型(Da ...