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 listsRemoteList = 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成員,但是並不是通過IOmxStore Service獲取的。
接下來就看看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" }