Android native MediaCodec编解码器模块化注册和创建处理流程
本系列文章分析的安卓源码版本:【Android 10.0 版本】
从【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】章节分析中涉及到了MediaCodec编解码器创建处理流程,因此有必要此章节单独分析编解码器创建流程,主要会涉及到OpenMAX框架的实现和交互。
推荐涉及到的知识点:
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来判断是否处理成功,
// 成功则直接返回当前可处理该mime类型格式的编解码器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];
// 初始化编解码器,检查是否初始化成功
// 见1.3小节分析
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