这节来了解下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进程中的全局变量。