1. 程式人生 > 其它 >Android 12(S) MultiMedia(十二)MediaCodecList & IOmxStore

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成員,但是並不是通過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"
      }