Android native MediaCodec编解码器模块化注册和创建处理流程
本系列文章分析的安卓源码版本:【Android 10.0 版本】
从【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】章节分析中涉及到了MediaCodec编解码器创建处理流程,因此有必要此章节单独分析编解码器创建流程,主要会涉及到OpenMAX框架的实现和交互。
TODO 未完待续
推荐涉及到的知识点:
Binder机制实现原理:Android C++底层Binder通信机制原理分析总结【通俗易懂】
ALooper机制实现原理:Android native层媒体通信架构AHandler/ALooper机制实现源码分析
Binder异常关闭监听:Android native层DeathRecipient对关联进程(如相关Service服务进程)异常关闭通知事件的监听实现源码分析
在start请求播放处理流程中有如下涉及代码:【省略其他代码】
有两种创建方式,format参数表示的是音频或视频track的媒体元数据信息(KV值)。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
CHECK(mCodec == NULL);
// 获取音频或视频mime格式
AString mime;
CHECK(format->findString("mime", &mime));
// 通过文件扩展格式mime判断
mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
// MEDIA_MIMETYPE_VIDEO_AVC:"video/avc"
mIsVideoAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str());
// 在该值后面添加decoder字符串
mComponentName = mime;
mComponentName.append(" decoder");
ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), mSurface.get());
// 创建MediaCodec解码器
// 第一种创建方式:根据文件扩展格式mime值
// 见第1小节分析
mCodec = MediaCodec::CreateByType(
mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid, mUid);
// 第二种创建方式:根据编码器组建名称
// 见第2小节分析
ALOGI("[%s] creating", mComponentName.c_str());
mCodec = MediaCodec::CreateByComponentName(
mCodecLooper, mComponentName.c_str(), NULL /* err */, mPid, mUid);
}
1、MediaCodec::CreateByType() 实现分析:
方法声明
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaCodec.h]
static const pid_t kNoPid = -1;
static const uid_t kNoUid = -1;
static sp<MediaCodec> CreateByType(
const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err = NULL,
pid_t pid = kNoPid, uid_t uid = kNoUid);
方法实现
// [frameworks/av/media/libstagefright/MediaCodec.cpp]
// static
sp<MediaCodec> MediaCodec::CreateByType(
const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
uid_t uid) {
Vector<AString> matchingCodecs;
// 根据方法名可知,此处为查询匹配可处理指定mime类型格式数据的编/解码器组件名集合,缓存在[matchingCodecs]中返回
// 见1.1小节分析
MediaCodecList::findMatchingCodecs(
mime.c_str(),
encoder,
0,
&matchingCodecs);
// err为调用端传入可用于接收错误码
if (err != NULL) {
*err = NAME_NOT_FOUND;
}
// 循环创建可处理组件名对应的MediaCodec对象,并执行init来判断是否处理成功,
// 成功则直接返回当前编解码器MediaCodec编解码处理对象。
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
// MediaCodec类声明和构造函数,见1.2小节分析
sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
AString componentName = matchingCodecs[i];
status_t ret = codec->init(componentName, true);
if (err != NULL) {
*err = ret;
}
if (ret == OK) {
return codec;
}
ALOGD("Allocating component '%s' failed (%d), try next one.",
componentName.c_str(), ret);
}
return NULL;
}
1.1、MediaCodecList::findMatchingCodecs() 实现分析:
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
//static
void MediaCodecList::findMatchingCodecs(
const char *mime, bool encoder, uint32_t flags,
Vector<AString> *matches) {
matches->clear();
// 先获取媒体编解码器列表操作对象【单列对象】
// 见1.1.1小节分析
const sp<IMediaCodecList> list = getInstance();
if (list == nullptr) {
return;
}
// 循环查询并匹配组件列表中支持当前mime类型格式的编解码器组件(插件)实例,
// 因此可以知道,支持同一种mime媒体格式类型的编码器或解码器可能会有多个不同实现,如软/硬编解码器组件。
size_t index = 0;
for (;;) {
// 获取支持该mime类型格式的编解码器组件在组件列表中的匹配索引
// 见1.1.2小节分析
ssize_t matchIndex =
list->findCodecByType(mime, encoder, index);
if (matchIndex < 0) {
// 若查询失败则表明列表中所有编解码器组件都不支持该mime类型数据的编解码
break;
}
// 循环查找下一个组件的索引
index = matchIndex + 1;
// 获取对应组件列表索引的组件信息实例
// 见1.1.3小节分析
const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
// 此次确保一定不能为空,否则报错
CHECK(info != nullptr);
// 获取当前匹配的组件名
AString componentName = info->getCodecName();
// 由前面调用传参可知,flags为0,而该标记位作用就是用来表示想使用的编解码器的类型,
// 比如此处的只匹配硬编解码器标志位。【关于此次的&与运算符计算就不再分析了,可自行先了解位运算】
// isSoftwareCodec() 实现,见1.1.4小节分析
if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
// 只匹配硬编解码器时,发现是软编解码器则skip跳过
ALOGV("skipping SW codec '%s'", componentName.c_str());
} else {
// 若无要求必须硬编解码器时,缓存此次循环匹配到的组件名到参数中返回
matches->push(componentName);
ALOGV("matching '%s'", componentName.c_str());
}
}
// 上面匹配完成后,再检查对匹配的编解码器组件排序问题,若设置了软编解码器优先级排序,
// 或设置了该系统属性值为true,那么将会执行软编解码器优先级排序处理。
if (flags & kPreferSoftwareCodecs ||
property_get_bool("debug.stagefright.swcodec", false)) {
// 软编解码器优先排序处理
// 见1.1.5小节分析
matches->sort(compareSoftwareCodecsFirst);
}
}
1.1.1、getInstance()实现分析:
获取媒体编解码器列表操作对象【单列对象】,获取方式Binder机制实现。
通过下面的分析可知:在远程列表对象创建失败时,将会直接调用本地创建实例方法getLocalInstance()的功能访问方式。
注意:
由下面的分析可知,其远程获取待实例的实现最终也是调用了该方法,但不同的是为什么要这么做,
其实原因就是使用不同的全局变量对象来缓存访问对象。一个是本地Bn实现端对象,一个是远程访问Bp代理端对象即就是实现了Binder机制的Bp代理端BpMediaCodecList对象,代理访问Bn实现端BnMediaCodecList子类MediaCodecList实现类功能而非直接访问对象功能。
因此若远程访问对象失败后将会创建直接访问功能的本地实例赋值给远程对象,此时的远程对象和本地对象将会是同一个对象。
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
// static
sp<IMediaCodecList> MediaCodecList::getInstance() {
// (远程初始化锁)加锁【多线程访问】
Mutex::Autolock _l(sRemoteInitMutex);
if (sRemoteList == nullptr) {
// 为空时
// 从ServiceManager服务注册管理中心获取名为【media.player】服务的Binder通信服务
sp<IBinder> binder =
defaultServiceManager()->getService(String16("media.player"));
// 最后使用interface_cast转换成对应服务取得Bp代理对象,
// 即获取到了BpMediaPlayerService实现,此处转换成它的父类类型返回。
sp<IMediaPlayerService> service =
interface_cast<IMediaPlayerService>(binder);
if (service.get() != nullptr) {
// 获取成功时【理论上都成功】
// 通过该服务实现来获取到了该远程媒体编解码器列表信息管理对象
// 见下面的分析
sRemoteList = service->getCodecList();
if (sRemoteList != nullptr) {
// 获取远程访问代理对象成功时
// 实现Binder异常死亡通知监听功能,见推荐章节分析。
sBinderDeathObserver = new BinderDeathObserver();
binder->linkToDeath(sBinderDeathObserver.get());
}
}
// 上面有可能失败,比如获取服务失败等,然后直接调用本地创建实例方法访问方式
if (sRemoteList == nullptr) {
// if failed to get remote list, create local list
sRemoteList = getLocalInstance();
}
}
return sRemoteList;
}
service->getCodecList() 实现分析:
此处不再讲述Binder机制处理过程(请查看推荐章节),直接进入Bn实现端BnMediaPlayerService子类MediaPlayerService实现者该方法分析,如下
【从实现可知,其远程获取待实例的实现最终也是调回了MediaCodecList的getLocalInstance()该方法】
// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
sp<IMediaCodecList> MediaPlayerService::getCodecList() const {
return MediaCodecList::getLocalInstance();
}
MediaCodecList::getLocalInstance() 本地实例对象创建实现:
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
// static
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
// (初始化锁)加锁【多线程访问】,注意此锁和上面远程获取处理流程不同。
Mutex::Autolock autoLock(sInitMutex);
// sCodecList 此变量即为全局缓存该对象变量
if (sCodecList == nullptr) {
// 还未初始化创建时
// 初始化该媒体编解码器列表管理对象,并且传入了参数 GetBuilders()
// GetBuilders()实现,见1.1.1.1小节分析
// MediaCodecList类声明和构造函数实现,见1.1.1.2小节分析
MediaCodecList *codecList = new MediaCodecList(GetBuilders());
// 此处实现很简单就是返回构造函数中 mInitCheck 初始化检查标记值是否为OK。
if (codecList->initCheck() == OK) {
// 初始化成功则缓存到本地全局变量对象中
sCodecList = codecList;
// 是否需要异步执行打印所有可用编解码器配置信息分析结果到指定xml配置文件,
// 该文件就是前面流程中分析的MediaCodecsXmlParser::defaultProfilingResultsXmlPath,默认为false。
// 见1.1.1.3小节分析
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;
}
new MediaCodecList(GetBuilders()) 实现分析:
1.1.1.1、首先需要分析参数实现,即GetBuilders()实现如下
【该方法整个实现方案相当于创建工厂设计模式或称为建造者模式,返回所有的编解码器创建工厂后续使用】
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
std::vector<MediaCodecListBuilderBase *> GetBuilders() {
// 媒体编解码器列表创建者集合
std::vector<MediaCodecListBuilderBase *> builders;
// 此处英文注释意思就是:
// 若StagefrightPluginLoader编解码器插件加载器提供了输入Surface对象即也就是提供了图形数据生产者时,
// 将不会使用OMX框架的视频解码器,在这种情况下,将依赖插件提供可用的OMX编解码器列表。
// 其实也就是插件也实现了加载OMX编解码器列表信息。
// 插件提供输入Surface表明不能使用视频或图像编码器,因为它的数据源是来自插件自身提供的Surface输入
// 而非OMX的输入Surface,因此跳过该插件(组件)配置信息。
// 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.
// 一般情况下,该对象为空,也就是说默认会加载编解码器插件,但createInputSurface()方法空实现,因此暂不分析此处。
// 编解码器插件实现是在【libsfplugin_ccodec.so】库中,该库源码在【frameworks/av/media/codec2/sfplugin/】文件夹下。
sp<PersistentSurface> surfaceTest =
StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();
if (surfaceTest == nullptr) {
// 因此会执行到此处
// sOmxInfoBuilder 该参数声明是全局缓存变量,
// 见下面的分析
ALOGD("Allowing all OMX codecs");
builders.push_back(&sOmxInfoBuilder);
} else {
// 此处执行,因此暂不分析
ALOGD("Allowing only non-surface-encoder OMX codecs");
builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder);
}
// GetCodec2InfoBuilder() 也会成功执行
// 见下面的分析
builders.push_back(GetCodec2InfoBuilder());
return builders;
}
sOmxInfoBuilder和sOmxNoSurfaceEncoderInfoBuilder声明:
目前先不分析其功能实现,后续将会分析。
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
OmxInfoBuilder sOmxInfoBuilder{
true /* allowSurfaceEncoders */};
OmxInfoBuilder sOmxNoSurfaceEncoderInfoBuilder{
false /* allowSurfaceEncoders */};
OmxInfoBuilderg类构造函数实现
// [frameworks/av/media/libstagefright/OmxInfoBuilder.cpp]
OmxInfoBuilder::OmxInfoBuilder(bool allowSurfaceEncoders)
: mAllowSurfaceEncoders(allowSurfaceEncoders) {
}
GetCodec2InfoBuilder() 实现分析:
获取编解码器创建者
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
Mutex sCodec2InfoBuilderMutex;
std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder;
MediaCodecListBuilderBase *GetCodec2InfoBuilder() {
// 加锁访问
Mutex::Autolock _l(sCodec2InfoBuilderMutex);
// 注意 sCodec2InfoBuilder 是一个C++标准std::unique_ptr全局缓存变量对象,因此有reset方法可访问
if (!sCodec2InfoBuilder) {
// 未初始化时初始化它
// 【关于C++的std::unique_ptr等智能指针标准库的实现,后续有时间可以分析,
// 其实和安卓实现的原理类似,但是计数器模式不一样】
// 见下面的分析
sCodec2InfoBuilder.reset(
StagefrightPluginLoader::GetCCodecInstance()->createBuilder());
}
// 将智能指针缓存的内部实际对象指针直接放回
return sCodec2InfoBuilder.get();
}
StagefrightPluginLoader::GetCCodecInstance()->createBuilder() 实现分析:
首先分析GetCCodecInstance()实现
如下实现可知,其实该插件加载器实际上是个so库即 kCCodecPluginPath 值为【libsfplugin_ccodec.so】,该so库是默认加载的。
// [frameworks/av/media/libstagefright/StagefrightPluginLoader.cpp]
constexpr const char kCCodecPluginPath[] = "libsfplugin_ccodec.so";
//static
const std::unique_ptr<StagefrightPluginLoader> &StagefrightPluginLoader::GetCCodecInstance() {
Mutex::Autolock _l(sMutex);
if (!sInstance) {
ALOGV("Loading library");
// 加载so库
sInstance.reset(new StagefrightPluginLoader(kCCodecPluginPath));
}
return sInstance;
}
StagefrightPluginLoader类声明:
// [frameworks/av/media/libstagefright/StagefrightPluginLoader.h]
class StagefrightPluginLoader {
}
StagefrightPluginLoader类构造函数实现:
// [frameworks/av/media/libstagefright/StagefrightPluginLoader.cpp]
StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) {
// 默认CCodec即该so库开启加载的。该值通常为4,该值的作用其实就是一个编解码器列表信息的排序模式。规定如何排序它们。
if (android::base::GetIntProperty("debug.stagefright.ccodec", 1) == 0) {
ALOGD("CCodec is disabled.");
return;
}
// 打开该so库,并获取到该so库访问句柄指针
mLibHandle = dlopen(libPath, RTLD_NOW | RTLD_NODELETE);
if (mLibHandle == nullptr) {
ALOGD("Failed to load library: %s (%s)", libPath, dlerror());
return;
}
// 读取so库中 CreateCodec 方法存储内存的方法开始地址,缓存在该方法类型的指针中
// 该方法类型指针声明,见下面的分析
mCreateCodec = (CodecBase::CreateCodecFunc)dlsym(mLibHandle, "CreateCodec");
if (mCreateCodec == nullptr) {
ALOGD("Failed to find symbol: CreateCodec (%s)", dlerror());
}
// 读取so库中 CreateBuilder 方法存储内存的方法开始地址,缓存在该方法类型的指针中
mCreateBuilder = (MediaCodecListBuilderBase::CreateBuilderFunc)dlsym(
mLibHandle, "CreateBuilder");
if (mCreateBuilder == nullptr) {
ALOGD("Failed to find symbol: CreateBuilder (%s)", dlerror());
}
// 读取so库中 CreateInputSurface 方法存储内存的方法开始地址,缓存在该方法类型的指针中
mCreateInputSurface = (CodecBase::CreateInputSurfaceFunc)dlsym(
mLibHandle, "CreateInputSurface");
if (mCreateInputSurface == nullptr) {
ALOGD("Failed to find symbol: CreateInputSurface (%s)", dlerror());
}
}
CreateCodec方法类型指针声明
// [frameworks/av/media/libstagefright/include/media/stagefright/CodecBase.h]
typedef CodecBase *(*CreateCodecFunc)(void);
// 类声明【省略其他代码】
// [frameworks/av/media/libstagefright/include/media/stagefright/CodecBase.h]
struct CodecBase : public AHandler, /* static */ ColorUtils {
}
// [frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation/ColorUtils.h]
// 该类其主要定义色彩空间类型值等
struct ColorUtils {
}
CreateBuilder方法类型指针声明
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h]
/**
* This interface is to be used by `MediaCodecList` to fill its members with
* appropriate information. `buildMediaCodecList()` will be called from a
* `MediaCodecList` object during its construction.
*/
struct MediaCodecListBuilderBase {
typedef MediaCodecListBuilderBase *(*CreateBuilderFunc)(void);
};
CreateInputSurface方法类型指针声明
将会创建一个持久化Surface。
// [frameworks/av/media/libstagefright/include/media/stagefright/CodecBase.h]
typedef PersistentSurface *(*CreateInputSurfaceFunc)(void);
// [frameworks/av/media/libstagefright/include/media/stagefright/PersistentSurface.h]
struct PersistentSurface : public RefBase {
}
再分析createBuilder()实现:
// [frameworks/av/media/libstagefright/StagefrightPluginLoader.cpp]
MediaCodecListBuilderBase *StagefrightPluginLoader::createBuilder() {
if (mLibHandle == nullptr || mCreateBuilder == nullptr) {
ALOGD("Handle or CreateBuilder symbol is null");
return nullptr;
}
// 由构造函数分析可知,此处就是直接执行so库中对应的方法。
return mCreateBuilder();
}
另外还有下面两个实现,也和上面这个相同处理:
// [frameworks/av/media/libstagefright/StagefrightPluginLoader.cpp]
CodecBase *StagefrightPluginLoader::createCodec() {
if (mLibHandle == nullptr || mCreateCodec == nullptr) {
ALOGD("Handle or CreateCodec symbol is null");
return nullptr;
}
return mCreateCodec();
}
PersistentSurface *StagefrightPluginLoader::createInputSurface() {
if (mLibHandle == nullptr || mCreateInputSurface == nullptr) {
ALOGD("Handle or CreateInputSurface symbol is null");
return nullptr;
}
return mCreateInputSurface();
}
此时我们再分析一下"libsfplugin_ccodec.so"该库实现中对应的"CreateBuilder"名称的方法实现,如下
【注意:由下面的实现可知,若是想要加载到so库的指的方法名称的方法访问地址指针,那么必现要保证该方法名不被C++编译器以C++编译方式来编译,因此必现使用C代码编译方式来编译即不会改变方法名】
// [frameworks/av/media/codec2/sfplugin/Codec2InfoBuilder.cpp]
// 标记该方法在C++编译器编译时必须使用C编译方式来编译到so库中。
extern "C" android::MediaCodecListBuilderBase *CreateBuilder() {
return new android::Codec2InfoBuilder;
}
Codec2InfoBuilder类声明及其构造函数实现:【省略其他代码】
// [frameworks/av/media/codec2/sfplugin/Codec2InfoBuilder.h]
class Codec2InfoBuilder : public MediaCodecListBuilderBase {
public:
// default修饰符表示该构造函数为默认无参构造实现,并没有重写自己实现。
Codec2InfoBuilder() = default;
~Codec2InfoBuilder() override = default;
}
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h]
/**
* This interface is to be used by `MediaCodecList` to fill its members with
* appropriate information. `buildMediaCodecList()` will be called from a
* `MediaCodecList` object during its construction.
*/
struct MediaCodecListBuilderBase {
}
因此由上面分析可知,getBuilders() 方法就创建好了可用于访问不同编解码器配置的”编解码器创建工厂“。
1.1.1.2、再分析MediaCodecList类声明和构造函数实现
由于该部分内容篇幅过长,因此放入另一章节分析,请查看:
Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 2】
1.1.1.3、isProfilingNeeded()实现分析:
是否需要异步执行打印所有可用编解码器配置结果信息到指定xml配置文件,该文件就是前面流程中分析的MediaCodecsXmlParser::defaultProfilingResultsXmlPath,默认为false。
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaCodecList.h]
static constexpr char const* defaultProfilingResultsXmlPath =
"/data/misc/media/media_codecs_profiling_results.xml";
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
constexpr const char* kProfilingResults =
MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
bool isProfilingNeeded() {
// 该属性作用:控制是否将系统中可用编解码器配置信息结果输出到指定xml配置文件中,默认未设置该属性
int8_t value = property_get_bool("debug.stagefright.profilecodec", 0);
if (value == 0) {
return false;
}
bool profilingNeeded = true;
// 打开该文件
// 警告:此次文件访问模式为"r"即打开一个用于读取的文件。但该文件必须存在。
// 默认该文件是不存在的,因此若想要查看结果信息必须自己创建该文件。
FILE *resultsFile = fopen(kProfilingResults, "r");
if (resultsFile) {
// 若打开成功
// 获取系统版本信息
AString currentVersion = getProfilingVersionString();
size_t currentVersionSize = currentVersion.size();
char *versionString = new char[currentVersionSize + 1];
fgets(versionString, currentVersionSize + 1, resultsFile);
if (strcmp(versionString, currentVersion.c_str()) == 0) {
// 此次判断是否已是最新,是根据系统版本来判断的。升级后就会更新它
// profiling result up to date
profilingNeeded = false;
}
fclose(resultsFile);
delete[] versionString;
}
return profilingNeeded;
}
getProfilingVersionString()实现:
也就是读取系统版本属性值
// [frameworks/av/media/libstagefright/MediaCodecListOverrides.cpp]
AString getProfilingVersionString() {
char val[PROPERTY_VALUE_MAX];
if (property_get("ro.build.display.id", val, NULL) && (strlen(val) > 0)) {
return AStringPrintf("", val);
}
return "";
}
1.1.2、list->findCodecByType(mime, encoder, index)实现分析:
获取支持给mime类型处理的编解码器组件在组件列表中的匹配索引
方法声明:
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaCodecList.h]
struct MediaCodecList : public BnMediaCodecList {
virtual ssize_t findCodecByType(
const char *type, bool encoder, size_t startIndex = 0) const;
}
方法实现:
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
// legacy method for non-advanced codecs
ssize_t MediaCodecList::findCodecByType(
const char *type, bool encoder, size_t startIndex) const {
// (编解码器)高级特性属性值
static const char *advancedFeatures[] = {
"feature-secure-playback",
"feature-tunneled-playback",
};
// 循环处理startIndex索引开始位置及其之后的所有编解码器组件信息
size_t numCodecInfos = mCodecInfos.size();
for (; startIndex < numCodecInfos; ++startIndex) {
const MediaCodecInfo &info = *mCodecInfos[startIndex];
// 判断请求的是编码器还是解码器处理
if (info.isEncoder() != encoder) {
// 不相同则跳过
continue;
}
// 获取encoder指定类型的编码器或解码器的能力信息
// 参数type就是mime类型即编解码器格式信息,匹配成功则返回该能力信息,否则为空
// 见下面的分析
sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
if (capabilities == nullptr) {
continue;
}
// 然后获取其详细能力信息属性值,对应于前面流程中分析的addDetails()方法添加的属性数据
const sp<AMessage> &details = capabilities->getDetails();
// 判断当前组件的支持属性值列表中是否包含上面两个高级特性属性值中其中任意一个,若包含则表示其为高级编解码器组件实现
int32_t required;
bool isAdvanced = false;
for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
// required值就是该高级属性对应的值
if (details->findInt32(advancedFeatures[ix], &required) &&
required != 0) {
isAdvanced = true;
break;
}
}
if (!isAdvanced) {
// 若没有设置启用这两个高级特性属性值的任意一个,
// 那么直接返回当前匹配到的组件在组件列表中的索引。
// NOTE:也就是说该方法查找的编解码器不应该支持这两个高级特性属性。若支持将被跳过。
// 并且若上面调用端处理成功后将会再次调用该方法,那么将会从此次组件位置的下一个组件开始匹配,
// 因为前面已匹配过的编解码器组件不用再次匹配了.
return startIndex;
}
}
// 若上面for循环查找未执行到那个return语句,也就表明没有可支持指定mime类型的编解码器组件
return -ENOENT;
}
info.getCapabilitiesFor(type)实现分析:
从下面两个方法的实现可知,其实就是获取此前初始化创建编解码器时执行的MediaCodecInfoWriter::addMediaType(),解析后获取的当前组件支持的mime格式类型,若匹配上则返回。并且匹配mime格式类型值是不分大小的。
// [frameworks/av/media/libmedia/MediaCodecInfo.cpp]
const sp<MediaCodecInfo::Capabilities>
MediaCodecInfo::getCapabilitiesFor(const char *mediaType) const {
ssize_t ix = getCapabilityIndex(mediaType);
if (ix >= 0) {
return mCaps.valueAt(ix);
}
return NULL;
}
getCapabilityIndex()实现:
// [frameworks/av/media/libmedia/MediaCodecInfo.cpp]
ssize_t MediaCodecInfo::getCapabilityIndex(const char *mediaType) const {
if (mediaType) {
for (size_t ix = 0; ix < mCaps.size(); ix++) {
if (mCaps.keyAt(ix).equalsIgnoreCase(mediaType)) {
return ix;
}
}
}
return -1;
}
1.1.3、list->getCodecInfo(matchIndex)实现分析:
获取对应组件列表索引的组件信息实例,其实就是列表下标访问即可。可能为空
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaCodecList.h]
virtual sp<MediaCodecInfo> getCodecInfo(size_t index) const {
if (index >= mCodecInfos.size()) {
ALOGE("b/24445127");
return NULL;
}
return mCodecInfos[index];
}
1.1.4、isSoftwareCodec(componentName)实现分析:
判断是否是软编解码器组件。
因此注意后续想要知道安卓编解码的软硬实现的判断,则可以看下这个实现。该实现也是随着版本不停变化的。
如下判断,若是该(插件编解码器)组件名开头字符串满足一下任意一个条件: “OMX.google.” 或 “c2.android.” 或 【非"OMX."】 或【非"c2."】,匹配字符串不分大小写,只要满足任意一个条件那么将被认为是软编解码器。
注意:前面流程中我们看到过组件名开头为 “OMX.qti” 的组件名配置信息,那么它将被认为是硬编解码器,而非xml配置文件中所描述的英文注释【video software】,关于这一点可以自行验证。
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
//static
bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
return componentName.startsWithIgnoreCase("OMX.google.")
|| componentName.startsWithIgnoreCase("c2.android.")
|| (!componentName.startsWithIgnoreCase("OMX.")
&& !componentName.startsWithIgnoreCase("c2."));
}
1.1.5、matches->sort(compareSoftwareCodecsFirst)实现分析:
软编解码器优先排序处理。如下英文注释三种排序处理,不再分析。
// [frameworks/av/media/libstagefright/MediaCodecList.cpp]
static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
// sort order 1: software codecs are first (lower)
bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1);
bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2);
if (isSoftwareCodec1 != isSoftwareCodec2) {
return isSoftwareCodec2 - isSoftwareCodec1;
}
// sort order 2: Codec 2.0 codecs are first (lower)
bool isC2_1 = name1->startsWithIgnoreCase("c2.");
bool isC2_2 = name2->startsWithIgnoreCase("c2.");
if (isC2_1 != isC2_2) {
return isC2_2 - isC2_1;
}
// sort order 3: OMX codecs are first (lower)
bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
return isOMX2 - isOMX1;
}
1.2、new MediaCodec(looper, pid, uid)实现分析:
MediaCodec类声明【省略其他代码】
MediaCodec类构造函数实现
1.3、实现分析:
由于该部分内容篇幅过长,因此放入另一章节分析,请查看:
Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 6】
TODO
2、MediaCodec::CreateByComponentName() 实现分析:
方法声明
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaCodec.h]
static sp<MediaCodec> CreateByComponentName(
const sp<ALooper> &looper, const AString &name, status_t *err = NULL,
pid_t pid = kNoPid, uid_t uid = kNoUid);
方法实现
// [frameworks/av/media/libstagefright/MediaCodec.cpp]