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成员,注意这个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进程中的全局变量。

 

posted @ 2022-04-21 16:29  青山渺渺  阅读(798)  评论(0编辑  收藏  举报