Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 2-B】

承接上一章节分析:Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 2-A】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

推荐涉及到的知识点:
Binder机制实现原理:Android C++底层Binder通信机制原理分析总结【通俗易懂】
ALooper机制实现原理:Android native层媒体通信架构AHandler/ALooper机制实现源码分析
Binder异常关闭监听:Android native层DeathRecipient对关联进程(如相关Service服务进程)异常关闭通知事件的监听实现源码分析

【此章节小节编号就接着上一章节排列】
media_codecs.xml 配置文件格式内容如下:【示例配置】
media_codecs.xml:该文件即是安卓平台编解码器信息参数的配置文件。
该文件在系统代码路径为:
device//common/media/media_codecs.xml 和 hardware//media/conf_files//media_codecs.xml
【vendor名比如qcom(高通),产品名称】
注意:这两个文件的区别,后一个路径是生成了特定产品的编解码配置信息(会多出一些在当前系统下测试出的编解码器性能参数配置值比如支持的最大分辨率、帧率),因此后一个是包含前一个的内容的。一般只需要看后一个。
备注:这些标签key值都将会在后续的编解码器配置信息加载过程中读取处理的。

<MediaCodecs>
	<!-- 此处可知,还引入了其他配置文件,如google内置的音视频软编解码器、telephony即【audio/gsm】音频软解码器 -->
    <Include href="media_codecs_google_audio.xml" />
    <Include href="media_codecs_google_telephony.xml" />
    <Encoders>
        <!-- Video Hardware  -->
        <MediaCodec name="OMX.qcom.video.encoder.avc" type="video/avc" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Quirk name="requires-loaded-to-idle-after-allocation" />
            <Limit name="size" min="96x64" max="4096x2160" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="979200" />
            <Limit name="bitrate" range="1-100000000" />
            <Limit name="frame-rate" range="1-240" />
            <Limit name="concurrent-instances" max="16" />
        </MediaCodec>
    </Encoders>
    <Decoders>
       <!-- Video Hardware  -->
        <MediaCodec name="OMX.qcom.video.decoder.avc" type="video/avc" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Limit name="size" min="64x64" max="4096x2160" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="1958400" />
            <Limit name="bitrate" range="1-100000000" />
            <Limit name="frame-rate" range="1-240" />
            <Feature name="adaptive-playback" />
            <Limit name="concurrent-instances" max="16" />
        </MediaCodec>
        <MediaCodec name="OMX.qcom.video.decoder.avc.secure" type="video/avc" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Limit name="size" min="64x64" max="4096x2160" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="1958400" />
            <Limit name="bitrate" range="1-100000000" />
            <Limit name="frame-rate" range="1-240" />
            <Feature name="adaptive-playback" />
            <Feature name="secure-playback" required="true" />
            <Limit name="concurrent-instances" max="6" />
        </MediaCodec>
    </Decoders>
    <Include href="media_codecs_google_video.xml" />
</MediaCodecs>

media_codecs_google_audio.xml 配置文件格式内容如下:【示例配置】
google内置的音频软编解码器

<Included>
    <Decoders>
        <MediaCodec name="OMX.google.aac.decoder" type="audio/mp4a-latm">
            <Limit name="channel-count" max="8" />
            <Limit name="sample-rate" ranges="7350,8000,11025,12000,16000,22050,24000,32000,44100,48000" />
            <Limit name="bitrate" range="8000-960000" />
        </MediaCodec>
    </Decoders>
    <Encoders>
        <MediaCodec name="OMX.google.aac.encoder" type="audio/mp4a-latm">
            <Limit name="channel-count" max="6" />
            <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
            <!-- also may support 64000, 88200  and 96000 Hz -->
            <Limit name="bitrate" range="8000-960000" />
        </MediaCodec>
    </Encoders>
</Included>

media_codecs_google_telephony.xml 配置文件格式内容如下:【示例配置】
google内置的telephony即【audio/gsm】音频软解码器

<Included>
    <Decoders>
        <MediaCodec name="OMX.google.gsm.decoder" type="audio/gsm">
            <Limit name="channel-count" max="1" />
            <Limit name="sample-rate" ranges="8000" />
            <Limit name="bitrate" range="13000" />
        </MediaCodec>
    </Decoders>
</Included>

media_codecs_google_video.xml 配置文件格式内容如下:【示例配置】
google内置的视频软编解码器

<Included>
    <Decoders>
        <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc">
            <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
            <Limit name="size" min="2x2" max="4096x4096" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="8x8" />
            <Limit name="block-count" range="1-196608" /> <!-- max 4096x3072 -->
            <Limit name="blocks-per-second" range="1-2000000" />
            <Limit name="bitrate" range="1-10000000" />
            <Feature name="adaptive-playback" />
        </MediaCodec>
    </Decoders>

    <Encoders>
        <MediaCodec name="OMX.google.h264.encoder" type="video/avc">
            <!-- profiles and levels:  ProfileBaseline : Level41 -->
            <Limit name="size" min="16x16" max="2048x2048" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="block-count" range="1-8192" /> <!-- max 2048x1024 -->
            <Limit name="blocks-per-second" range="1-245760" />
            <Limit name="bitrate" range="1-12000000" />
            <Feature name="intra-refresh" />
        </MediaCodec>
    </Encoders>
</Included>

media_codecs_performance.xml 配置文件格式内容如下:【示例配置】
该文件是安卓平台视频编解码器在当前系统下测试出的编解码器支持分辨率及其对应帧率性能参数值。
【注意此文件只测试了视频编解码器性能】
该文件在系统代码路径为:【只需要看特定产品的配置文件即可】
hardware//media/conf_files//media_codecs_performance.xml
【vendor名比如qcom(高通),产品名称】

<MediaCodecs>
    <Encoders>
        <MediaCodec name="OMX.qcom.video.encoder.hevc" type="video/hevc" update="true">
            <Limit name="measured-frame-rate-320x240" range="347-355" />
            <Limit name="measured-frame-rate-720x480" range="190-192" />
            <Limit name="measured-frame-rate-1280x720" range="100-105" />
            <Limit name="measured-frame-rate-1920x1080" range="33-49" />
            <Limit name="measured-frame-rate-3840x2160" range="11-16" />
        </MediaCodec>
    </Encoders>
    <Decoders>
        <MediaCodec name="OMX.qcom.video.decoder.avc" type="video/avc" update="true">
            <Limit name="measured-frame-rate-320x240" range="169-267" />
            <Limit name="measured-frame-rate-720x480" range="194-249" />
            <Limit name="measured-frame-rate-1280x720" range="136-194" />
            <Limit name="measured-frame-rate-1920x1088" range="109-142" />
        </MediaCodec>
    </Decoders>
</MediaCodecs>

OmxStore类构造函数实现

// [frameworks/av/media/libstagefright/omx/1.0/OmxStore.cpp]
OmxStore::OmxStore(
        const sp<IOmx> &omx,
        const char* owner,
        const std::vector<std::string> &searchDirs,
        const std::vector<std::string> &xmlNames,
        const char* profilingResultsXmlPath) {
     
    // 检索omx(中)节点信息列表变量
    // retrieve list of omx nodes
    std::set<std::string> nodes;
    if (omx != nullptr) {
    	// omx对象不为空时将会获取该节点信息列表值。
    	// 那么我们来分析一下IOmx的大致实现,首先通过实现和前面分析可知,
    	// IOmx头文件引入方式:
    	// #include <android/hardware/media/omx/1.0/IOmx.h>,
    	// 使用其类方法为:
    	// using ::android::hardware::media::omx::V1_0::IOmx;
    	// 因此我们知道,该类头文件原始文件定义也是使用HIDL语言实现的hal文件。
    	// 根据上述我们分析过HIDL大致加载流程,我们可以确定其实际子类名叫Omx,其实现文件肯定是Omx.h和Omx.cpp。
    	// 也就是说它的功能提供也是个so库提供的。
    	
    	// Omx类声明和构造函数实现,见1.1.1小节分析
    	// Omx的listNodes()方法实现,其参数是一个std::function类型的匿名方法,见1.1.2小节分析
    	// nodes参数是传给listNodes()方法的参数
        omx->listNodes([&nodes](const Status &status,
                                const hidl_vec<IOmx::ComponentInfo> &nodeList) {
            if (status == Status::OK) {
            	// 获取成功时,for循环取出插件编解码器组件信息的组件名,例如"OMX.google.hevc.decoder"
            	// 并缓存在nodes节点信息列表中
                for (const IOmx::ComponentInfo& info : nodeList) {
                    nodes.emplace(info.mName.c_str());
                }
            }
        });
    }

    // 由前面流程的分析发现,接下里三个方法的处理都是此前介绍过的,
    // 也就是解析媒体编解码器xml配置文件中的信息。
    // 备注:关于解析出来的结果值可以对照着前面流程中分析的音视频编解码器几个xml配置文件中的信息来看
    MediaCodecsXmlParser parser;
    parser.parseXmlFilesInSearchDirs(xmlNames, searchDirs);
    if (profilingResultsXmlPath != nullptr) {
        parser.parseXmlPath(profilingResultsXmlPath);
    }
    // 转换媒体编解码器xml解析器解析状态并缓存
    mParsingStatus = toStatus(parser.getParsingStatus());

    // 获取解析出来的服务属性映射值,并for循环缓存在列表mServiceAttributeList中
    const auto& serviceAttributeMap = parser.getServiceAttributeMap();
    mServiceAttributeList.resize(serviceAttributeMap.size());
    size_t i = 0;
    for (const auto& attributePair : serviceAttributeMap) {
        ServiceAttribute attribute;
        attribute.key = attributePair.first;
        attribute.value = attributePair.second;
        mServiceAttributeList[i] = std::move(attribute);
        ++i;
    }

    // 获取解析出来的节点角色信息映射值,然后for循环进行数据类型转换和检查数据有效性,并缓存
    const auto& roleMap = parser.getRoleMap();
    mRoleList.resize(roleMap.size());
    i = 0;
    for (const auto& rolePair : roleMap) {
        RoleInfo role;
        role.role = rolePair.first;
        role.type = rolePair.second.type;
        role.isEncoder = rolePair.second.isEncoder;
        role.preferPlatformNodes = false; // deprecated and ignored, using rank instead
        hidl_vec<NodeInfo>& nodeList = role.nodes;
        nodeList.resize(rolePair.second.nodeList.size());
        size_t j = 0;
        for (const auto& nodePair : rolePair.second.nodeList) {
        	// 检查从xml中读取的组件名和注册的编解码器组件名。
        	// nodes为前面IOmx中获取到的具体编解码器组件名列表信息
        	// std::set 是关联容器,含有 Key 类型对象的已排序集。
        	// count方法计算返回匹配特定键的元素数量
            if (!nodes.count(nodePair.second.name)) {
            	// 匹配失败时,若以"omx."小写字符串开头的组件名则直接跳过忽略xml配置文件中的该插件信息
                // not supported by this OMX instance
                if (!strncasecmp(nodePair.second.name.c_str(), "omx.", 4)) {
                    LOG(INFO) << "node [" << nodePair.second.name.c_str() << "] not found in IOmx";
                }
                continue;
            }
            NodeInfo node;
            node.name = nodePair.second.name;
            // 编解码器归属者信息,注意该值比较重要,在后续创建Codec时会用其归属来判断创建,此处该值默认为"default"
            node.owner = owner;
            hidl_vec<NodeAttribute>& attributeList = node.attributes;
            attributeList.resize(nodePair.second.attributeList.size());
            size_t k = 0;
            for (const auto& attributePair : nodePair.second.attributeList) {
                NodeAttribute attribute;
                attribute.key = attributePair.first;
                attribute.value = attributePair.second;
                attributeList[k] = std::move(attribute);
                ++k;
            }
            nodeList[j] = std::move(node);
            ++j;
        }
        nodeList.resize(j);
        // 缓存转换后的目标数据类型到列表中
        mRoleList[i] = std::move(role);
        ++i;
    }
	
	// 获取所有组件节点名的公共前缀。
	// 备注:该信息不是xml配置的,而是通过计算得到的。
    mPrefix = parser.getCommonPrefix();
}

1.1.1、Omx 类声明和构造函数实现:
由于该部分内容篇幅过长,因此放入另一章节分析,请查看
Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 3】

1.1.2、Omx的listNodes()方法实现分析:
根据前面章节和小节分析可知,该方法的调用是Binder机制跨进程实现的。
该方法声明:

// [frameworks/av/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h]
    // Methods from IOmx
    Return<void> listNodes(listNodes_cb _hidl_cb) override;

其是来自于父类声明:【纯虚函数】
其父类为IOmx.h,其实际也是使用HIDL语言的IOmx.hal文件编译时自动生成的Binder机制相关头文件,关于HIDL请查看前面分析。此处可以看到该方法的参数listNodes_cb实际是一个可传入列表节点信息参数集合(Status和ComponentInfo)的std::function类型方法回调参数,即传入参数是一个返回值为void的匿名方法,这些列表节点信息参数类型值其实是HIDL语言自身定义的一套用于自动实现Binder机制的转换数据类型,不展开分析。
自动生成的头文件:/out/xxx/.intermediates/hardware/interfaces/media/omx/1.0/android.hardware.media.omx@1.0_genc++_headers/gen/android/hardware/media/omx/1.0/IOmx.h【xxx为具体平台】

    /**
     * Return callback for listNodes
     */
    using listNodes_cb = std::function<void(::android::hardware::media::omx::V1_0::Status status, const ::android::hardware::hidl_vec<::android::hardware::media::omx::V1_0::IOmx::ComponentInfo>& nodeList)>;
    /**
     * List available components.
     *
     * @return status The status of the call.
     * @return nodeList The list of ComponentInfo.
     */
    virtual ::android::hardware::Return<void> listNodes(listNodes_cb _hidl_cb) = 0;

直接分析Bn实现端:
获取编解码器组件信息列表

// [frameworks/av/media/libstagefright/omx/1.0/Omx.cpp]
Return<void> Omx::listNodes(listNodes_cb _hidl_cb) {
    std::list<::android::IOMX::ComponentInfo> list;
    char componentName[256];
    // 也是使用for循环获取插件实现的编解码器组件信息列表
    // mMaster->enumerateComponents()实现,见1.1.2.1小节分析
    // 备注:组件名不能超过256个字符
    for (OMX_U32 index = 0;
            mMaster->enumerateComponents(
            componentName, sizeof(componentName), index) == OMX_ErrorNone;
            ++index) {
        // 读取成功时,将放入一个新的缓存组件信息结构匿名空对象到组件信息列表中
        // IOMX::ComponentInfo结构声明,见1.1.2.2小节分析
        list.push_back(::android::IOMX::ComponentInfo());
        // 取出该新对象
        ::android::IOMX::ComponentInfo& info = list.back();
        // 缓存组件名
        info.mName = componentName;
        ::android::Vector<::android::String8> roles;
        // 根据组件名获取组件节点角色信息,元素值示例:"audio_decoder.aac"
        // 见1.1.2.3小节分析
        OMX_ERRORTYPE err =
                mMaster->getRolesOfComponent(componentName, &roles);
        if (err == OMX_ErrorNone) {
        	// 获取成功时
        	// 循环将该列表信息放入组件信息结构中缓存
        	// 备注:其实由下面的分析可知,在安卓原生插件so库实现中每次只会返回一个节点角色信息值
            for (OMX_U32 i = 0; i < roles.size(); ++i) {
                info.mRoles.push_back(roles[i]);
            }
        }
    }

    // 此处for循环将组件信息列表对象类型转换为HIDL可传递的列表对象类型
    hidl_vec<ComponentInfo> tList;
    tList.resize(list.size());
    size_t i = 0;
    for (auto const& info : list) {
    	// 将IOMX::ComponentInfo类型数据转换为IOmx::ComponentInfo
    	// 见1.1.2.4小节分析
        convertTo(&(tList[i++]), info);
    }
    // 然后执行传入的回调方法参数,并返回参数结果值
    // toStatus(OK)该方法也是转换数据类型功能,见1.1.2.5小节分析
    _hidl_cb(toStatus(OK), tList);
    return Void();
}

1.1.2.1、mMaster->enumerateComponents()实现分析:
和前面流程分析中功能类似处理,枚举获取每个指定索引的编解码器组件信息

// [frameworks/av/media/libstagefright/omx/OMXMaster.cpp]
OMX_ERRORTYPE OMXMaster::enumerateComponents(
        OMX_STRING name,
        size_t size,
        OMX_U32 index) {
    // 加锁访问,因为由前面流程的分析可知,在添加组件到列表中时需要加锁的
    Mutex::Autolock autoLock(mLock);

    // 由前面流程的分析可知,该变量即为缓存的插件组件名列表信息
    size_t numComponents = mPluginByComponentName.size();

	// 检查传入列表索引值
    if (index >= numComponents) {
        return OMX_ErrorNoMore;
    }

    // 读取当前插件索引的组件名
    const String8 &name8 = mPluginByComponentName.keyAt(index);

	// 检查当前传入参数name字符串变量能够缓存字符个数大小size必须大于等于当前读取到的组件名大小加上1的值,
	// 加1的原因是最后一位必须是空字符,这样name才是字符串,否则会使数组。
    CHECK(size >= 1 + name8.size());
    // 复制插件编解码器组件名返回
    strcpy(name, name8.string());

    return OMX_ErrorNone;
}

1.1.2.2、IOMX::ComponentInfo结构声明:
插件编解码器组件信息结构,其是IOMX类的内部类

// [frameworks/av/media/libmedia/include/media/IOMX.h]
class IOMX : public RefBase {
    struct ComponentInfo {
    	// 组件名
        String8 mName;
        // 组件节点角色信息,可参考前面流程中的分析
        List<String8> mRoles;
    };
}

1.1.2.3、mMaster->getRolesOfComponent(componentName, &roles)实现分析:
根据组件名获取组件节点角色信息

// [frameworks/av/media/libstagefright/omx/OMXMaster.cpp]
OMX_ERRORTYPE OMXMaster::getRolesOfComponent(
        const char *name,
        Vector<String8> *roles) {
    // 加锁访问
    Mutex::Autolock autoLock(mLock);

    // 清空
    roles->clear();

    // 根据前面流程中的分析,此处将会获取到指定插件编解码器组件名实现的编解码器插件具体对象指针
    ssize_t index = mPluginByComponentName.indexOfKey(String8(name));

    // 检查获取是否存在,索引小于0则不存在,返回无效组件名错误码
    if (index < 0) {
        return OMX_ErrorInvalidComponentName;
    }

    // 获取该组件名对应的编解码器具体对象指针
    // 备注:该对象的创建过程请看前面流程中相关分析,在加载每个插件时初始化的,
    // 并且目前该插件对象有两个即QComOMXPlugin和SoftOMXPlugin。
    OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
    // 然后执行每个插件该方法的具体实现,此处将会分析SoftOMXPlugin的实现,
    // 关于高通实现以后有时间再分析吧,实现类似原理
    return plugin->getRolesOfComponent(name, roles);
}

plugin->getRolesOfComponent(name, roles)实现分析:
SoftOMXPlugin的该方法具体实现

// [frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp]
OMX_ERRORTYPE SoftOMXPlugin::getRolesOfComponent(
        const char *name,
        Vector<String8> *roles) {
    // kNumComponents和kComponents定义,见前面流程中的分析
    // for循环来匹配知道组件名的编解码器的节点角色信息
    for (size_t i = 0; i < kNumComponents; ++i) {
    	// strcmp返回0才表示字符串相等
        if (strcmp(name, kComponents[i].mName)) {
            continue;
        }

		// 先清空角色信息列表变量,再缓存
        roles->clear();
        roles->push(String8(kComponents[i].mRole));

        return OMX_ErrorNone;
    }

    // 未找到,返回无效组件名错误码
    return OMX_ErrorInvalidComponentName;
}

1.1.2.4、convertTo(&(tList[i++]), info)实现:
将IOMX::ComponentInfo类型数据转换为IOmx::ComponentInfo

// [frameworks/av/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h]

/**
 * \brief Convert `IOMX::ComponentInfo` to `IOmx::ComponentInfo`.
 *
 * \param[out] t The destination `IOmx::ComponentInfo`.
 * \param[in] l The source `IOMX::ComponentInfo`.
 */
// convert: IOMX::ComponentInfo -> IOmx::ComponentInfo
inline bool convertTo(IOmx::ComponentInfo* t, IOMX::ComponentInfo const& l) {
    t->mName = l.mName.string();
    t->mRoles.resize(l.mRoles.size());
    size_t i = 0;
    for (auto& role : l.mRoles) {
        t->mRoles[i++] = role.string();
    }
    return true;
}

1.1.2.5、toStatus(OK)实现:
该方法也是转换数据类型功能

// [frameworks/av/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h]
/**
 * \brief Convert `status_t` to `Status`.
 *
 * \param[in] l The source `status_t`.
 * \return The corresponding `Status`.
 */
// convert: status_t -> Status
inline Status toStatus(status_t l) {
    switch (l) {
    case NO_ERROR:
    case NAME_NOT_FOUND:
    case WOULD_BLOCK:
    case NO_MEMORY:
    case ALREADY_EXISTS:
    case NO_INIT:
    case BAD_VALUE:
    case DEAD_OBJECT:
    case INVALID_OPERATION:
    case TIMED_OUT:
    case ERROR_UNSUPPORTED:
    case UNKNOWN_ERROR:
    case BufferQueueDefs::RELEASE_ALL_BUFFERS:
    case BufferQueueDefs::BUFFER_NEEDS_REALLOCATION:
    	// 强制转换成另一个枚举类型,会先把它提升为int值再转换对应值的枚举类型
        return static_cast<Status>(l);
    // 数据不足状态码转换    
    case NOT_ENOUGH_DATA:
        return Status::BUFFER_NEEDS_REALLOCATION;
    default:
        ALOGW("Unrecognized status value: %" PRId32, static_cast<int32_t>(l));
        return static_cast<Status>(l);
    }
}

自此,1.1小节内容已分析结束。关于HIDL实现机制原理,再后续由时间时再详细分析吧。

1.2、OmxStore的listRoles()方法实现分析:
通过Binder机制调用Bn实现端OmxStore的该方法,如下根据1.1中的类似方法实现分析可知,参数为std::function()类型匿名方法,并执行它携带参数mRoleList返回给调用端。而mRoleList变量信息在1.1中已分析过,其内容为插件编解码器组件节点角色信息列表。

// [frameworks/av/media/libstagefright/omx/1.0/OmxStore.cpp]
Return<void> OmxStore::listRoles(listRoles_cb _hidl_cb) {
    _hidl_cb(mRoleList);
    return Void();
}

1.3、omxStore->listServiceAttributes() 实现分析:
获取组件服务属性列表信息

// [frameworks/av/media/libstagefright/omx/1.0/OmxStore.cpp]
Return<void> OmxStore::listServiceAttributes(listServiceAttributes_cb _hidl_cb) {
    // 根据1.1小节分析可知,解析状态成功时返回对应状态和缓存的服务属性列表值,否则返回错误码,并返回一个空列表对象
    if (mParsingStatus == Status::NO_ERROR) {
        _hidl_cb(Status::NO_ERROR, mServiceAttributeList);
    } else {
        _hidl_cb(mParsingStatus, hidl_vec<ServiceAttribute>());
    }
    return Void();
}

1.4、writer->addGlobalSetting()实现分析:
添加组件节点服务属性值即KV键值对(全局配置信息也就是此前分析过的音视频编解码器配置xml中相关信息)

// [frameworks/av/media/libstagefright/MediaCodecListWriter.cpp]
void MediaCodecListWriter::addGlobalSetting(
        const char* key, const char* value) {
    // 全局设置信息对象定义为:
    // std::vector<std::pair<std::string, std::string>> mGlobalSettings;
    // 即它就是相当于键值对map对象功能,缓存键值对列表信息
    mGlobalSettings.emplace_back(key, value);
}

1.5、hasPrefix()实现:
其实实现很简单,就是判断给定字符串开始字符串是否以前缀字符串开始,若是则返回true。注意此处判断前缀字符串是大小写字符都可以。

// [frameworks/av/media/libstagefright/OmxInfoBuilder.cpp]
bool hasPrefix(const hidl_string& s, const char* prefix) {
    return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
}

1.6、 writer->addMediaCodecInfo() 实现分析:

// [frameworks/av/media/libstagefright/MediaCodecListWriter.cpp]
std::unique_ptr<MediaCodecInfoWriter>
        MediaCodecListWriter::addMediaCodecInfo() {
    // 该处理实际上就是,创建一个编解码器组件信息空对象添加到列表mCodecInfos中, 
    // 并将它封装到MediaCodecInfoWriter对象中返回。
    // MediaCodecInfo和MediaCodecInfoWriter类声明和构造函数实现,见下面的分析
    sp<MediaCodecInfo> info = new MediaCodecInfo();
    mCodecInfos.push_back(info);
    return std::unique_ptr<MediaCodecInfoWriter>(
            new MediaCodecInfoWriter(info.get()));
}

MediaCodecInfo类声明和构造函数实现:
MediaCodecInfo类声明【省略其他代码】

// [frameworks/av/media/libmedia/include/media/MediaCodecInfo.h]
struct MediaCodecInfo : public RefBase {}

MediaCodecInfo类构造函数实现

// [frameworks/av/media/libmedia/MediaCodecInfo.cpp]
MediaCodecInfo::MediaCodecInfo()
	// 属性bit位标志位,也就是它可以表示多种类型,见下面该枚举定义,默认为0即无效值
    : mAttributes((MediaCodecInfo::Attributes)0),
    // 默认排序类型
      mRank(0x100) {
}

编解码器属性类型,即如下,是否是编码器,是否属于vendor,是否仅仅是软实现,是否支持硬件加速

// [frameworks/av/media/libmedia/include/media/MediaCodecInfo.h]
struct MediaCodecInfo : public RefBase {
    enum Attributes : int32_t {
        // attribute flags
        kFlagIsEncoder = 1 << 0,
        kFlagIsVendor = 1 << 1,
        kFlagIsSoftwareOnly = 1 << 2,
        kFlagIsHardwareAccelerated = 1 << 3,
    };
}

MediaCodecInfoWriter类声明和构造函数实现:
MediaCodecInfoWriter类声明【省略其他代码】

// [frameworks/av/media/libmedia/include/media/MediaCodecInfo.h]

/**
 * This class is to be used by a `MediaCodecListBuilderBase` instance to
 * populate information inside the associated `MediaCodecInfo` object.
 *
 * The only place where an instance of `MediaCodecInfoWriter` can be constructed
 * is `MediaCodecListWriter::addMediaCodecInfo()`. A `MediaCodecListBuilderBase`
 * instance should call `MediaCodecListWriter::addMediaCodecInfo()` on the given
 * `MediaCodecListWriter` object given as an input to
 * `MediaCodecListBuilderBase::buildMediaCodecList()`.
 */
struct MediaCodecInfoWriter {}

MediaCodecInfoWriter类构造函数实现
缓存MediaCodecInfo参数

// [frameworks/av/media/libmedia/MediaCodecInfo.cpp]
MediaCodecInfoWriter::MediaCodecInfoWriter(MediaCodecInfo* info) :
    mInfo(info) {
}

1.7、MediaCodecInfo::Capabilities 能力信息属性声明:
这些属性值是声明在每个编解码器xml配置信息中的。

// [frameworks/av/media/libmedia/include/media/MediaCodecInfo.h]
struct MediaCodecInfo : public RefBase {
    struct Capabilities : public RefBase {
        constexpr static char FEATURE_ADAPTIVE_PLAYBACK[] = "feature-adaptive-playback";
        constexpr static char FEATURE_DYNAMIC_TIMESTAMP[] = "feature-dynamic-timestamp";
        constexpr static char FEATURE_FRAME_PARSING[] = "feature-frame-parsing";
        constexpr static char FEATURE_INTRA_REFRESH[] = "feature-frame-parsing";
        constexpr static char FEATURE_MULTIPLE_FRAMES[] = "feature-multiple-frames";
        constexpr static char FEATURE_SECURE_PLAYBACK[] = "feature-secure-playback";
        constexpr static char FEATURE_TUNNELED_PLAYBACK[] = "feature-tunneled-playback";
    }
}    

1.8、queryCapabilities(node, typeName.c_str(), isEncoder, caps.get())实现分析:
查询该codec的支持的能力信息

// [frameworks/av/media/libstagefright/OmxInfoBuilder.cpp]
status_t queryCapabilities(
        const IOmxStore::NodeInfo& node, const char* mediaType, bool isEncoder,
        MediaCodecInfo::CapabilitiesWriter* caps) {
    // 创建一个新的ACodec对象
    // 备注:该类也是非常重要的实现。该类声明和构造函数实现将在另外章节分析。
    sp<ACodec> codec = new ACodec();
    // for循环处理编解码器特殊属性值
    for (const auto& attribute : node.attributes) {
        // All features have an int32 value except
        // "feature-bitrate-modes", which has a string value.
        if (hasPrefix(attribute.key, "feature-") &&
                !hasPrefix(attribute.key, "feature-bitrate-modes")) {
            // If this attribute.key is a feature that is not bitrate modes,
            // add an int32 value.
            caps->addDetail(
                    attribute.key.c_str(),
                    hasPrefix(attribute.value, "1") ? 1 : 0);
        } else {
            // Non-feature attributes
            // 非feature属性值时直接缓存原属性值
            caps->addDetail(
                    attribute.key.c_str(), attribute.value.c_str());
        }
    }
    // 查询编解码器支持能力信息,若实际编解码器插件组件实现不支持则该能力信息会被移除。
    // query capabilities may remove capabilities that are not actually supported by the codec
    status_t err = codec->queryCapabilities(
            node.owner.c_str(), node.name.c_str(), mediaType, isEncoder, caps);
    if (err != OK) {
        return err;
    }
    return OK;
}

codec->queryCapabilities()实现分析:
查询编解码器支持能力信息,若实际编解码器插件组件实现不支持则该能力信息会被移除。

暂不分析TODO 关于ACodec类声明和构造函数会在后续处理流程中重点分析,因此此处的queryCapabilities()方法实现在后续流程分析完成后再回来分析此处。

由于该部分内容篇幅过长,因此放入另一章节分析,请查看:
Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 4】
TODO 放入链接

再分析sCodec2InfoBuilder的buildMediaCodecList(&writer)实现分析:
读取编解码器配置信息并存入writer中返回。
由于该部分内容篇幅过长,因此放入另一章节分析,请查看:
Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 5】

2、writer.writeGlobalSettings(mGlobalSettings)实现分析:
将组件节点服务属性值(KV键值对)列表mGlobalSettings中缓存的信息写入传入的参数对象中。mGlobalSettings该缓存列表信息由第1小节流程中添加的。

// [frameworks/av/media/libstagefright/MediaCodecListWriter.cpp]
void MediaCodecListWriter::writeGlobalSettings(
        const sp<AMessage> &globalSettings) const {
    for (const std::pair<std::string, std::string> &kv : mGlobalSettings) {
        globalSettings->setString(kv.first.c_str(), kv.second.c_str());
    }
}

3、writer.writeCodecInfos(&mCodecInfos)实现分析:
mCodecInfos为第1小节中添加的编解码器配置信息全部放入在参入列表参数对象指针中返回。

// [frameworks/av/media/libstagefright/MediaCodecListWriter.cpp]
void MediaCodecListWriter::writeCodecInfos(
        std::vector<sp<MediaCodecInfo>> *codecInfos) const {
    for (const sp<MediaCodecInfo> &info : mCodecInfos) {
        codecInfos->push_back(info);
    }
}

本章节结尾:若您是按照文章小节流程分析来看的话,那么本章节此处即为本系列内容分析基本完成。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值