Android底层音视频播放媒体提取器【MediaExtractor】的解复用模块demuxers模块化加载和注册流程实现源码分析【Part 2】

本文详细分析了Android10.0版本中MediaExtractor的解复用模块加载和注册流程,特别是高通模块libmmparserextractor的实现。内容包括GetExtractorDef方法指针的查找,Sniff方法的实现,以及GetExtensions方法的执行过程,揭示了Android系统如何识别和处理不同音视频文件格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

承接上一章节分析:Android底层音视频播放媒体提取器【MediaExtractor】的解复用模块demuxers模块化加载和注册流程实现源码分析【Part 1】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

关于每个so库提取器具体如何实现GetExtractorDef方法指针,这里只列举一下高通该功能的实现,其他so库实现方式都是类似的。
首先我们可以找到高通模块so库名称【libmmparserextractor】的声明mk文件:
动态库创建的mk文件声明。【省略部分代码】

// [vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/Android.mk]
BUILD_PARSER :=

include $(CLEAR_VARS)

LOCAL_C_FLAGS := -D_ANDROID_

LOCAL_SRC_FILES:=                 \
    src/MMParserExtractor.cpp     \
    src/QComExtractorFactory.cpp  \
    src/SourcePort.cpp            \

LOCAL_C_INCLUDES:=                                    \
    $(LOCAL_PATH)/inc                                 \
    $(LOCAL_PATH)/../../common/inc                    \
    $(LOCAL_PATH)/../../../common/inc                 \
    $(TARGET_OUT_HEADERS)/mm-core/omxcore

LOCAL_HEADER_LIBRARIES :=       \
    libmmosal_headers           \
    libmmparser_headers         \
    libstagefright_headers

LOCAL_SHARED_LIBRARIES +=       \
    libcutils                   \
    liblog                      \
    libmediandk                 \
    libmmparser_lite            \
    libstagefright              \
    libstagefright_foundation   \
    libutils

LOCAL_SANITIZE := cfi signed-integer-overflow unsigned-integer-overflow

LOCAL_MODULE:= libmmparserextractor

LOCAL_MODULE_RELATIVE_PATH := extractors

LOCAL_MODULE_TAGS := optional

include $(BUILD_SHARED_LIBRARY)
endif

因此可以知道该mk包含的实现文件就是该so库的实现。
然后找到实现了GetExtractorDef方法指针的实现方法。
注意:[QComExtractorFactory.cpp]该文件内的实现都是在android命名空间中,头文件[frameworks/av/include/media/MediaExtractorPluginApi.h]也在它的实现也是在android命名空间中,因此实现端必须引用该头文件。
再注意此处是要求C++编译器通过C编译器编译方式来编译这段代码,也就是说方法名【GETEXTRACTORDEF】在编译后的库中将不会被修改(注:C++编译方式就会改变该值),因此也就能像上面的分析一样可以通过dlsym来查询指定名【GETEXTRACTORDEF】的该方法指针(即方法地址)。

// [vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/src/QComExtractorFactory.cpp]
extern "C" {
   

// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
ExtractorDef GETEXTRACTORDEF() {
   
    return {
   
        EXTRACTORDEF_VERSION,
        UUID("2cb1d938-46d8-436d-b73e-681c47392cc8"),
        2, // version
        "QCOM Extractor",
        // 并实现了这两个方法
        // Sniff方法指针实现,见2.1小节分析
        // GetExtensions() 方法实现,见2.2小节分析
        {
    .v3 = {
   Sniff, GetExtensions()} }
    };
}

} // extern "C"

2.1、Sniff() 方法实现:

// [vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/src/QComExtractorFactory.cpp]
static CreatorFunc Sniff(CDataSource *source, float *confidence,
                                         void **, FreeMetaFunc *) {
   
    // 该类其实是一个CDataSource类的代理实现类,主要就是读取数据源的数据(注意非解复用,仅仅只是读取数据),
    // CDataSource类对象由上层调用端实现和传入。
    // 其定义在[/frameworks/av/include/media/MediaExtractorPluginHelper.h]头文件中
    DataSourceHelper helper(source);
    // 分配一个指向128KB大小的指针,MAX_FORMAT_HEADER_SIZE 宏定义大小为128KB
    uint8_t* sniffBuffer = (uint8_t*)malloc(sizeof(uint8_t) * MAX_FORMAT_HEADER_SIZE);
    if (!sniffBuffer)
    {
   
        LOGE("malloc for sniff buffer failed");
        return NULL;
    }

    bool sniffSuccess = false;
    bool isEOS = false;
    int  parser_flags = 0;
    char property_value[PROPERTY_VALUE_MAX] = {
   0};
    // 文件数据源读取状态
    FileSourceStatus status = FILE_SOURCE_INVALID;
    ssize_t sniffBufferSize = 0;
    uint32_t requiredSize = 0;
    // GetID3Size() 方法是处理获取ID3数据的偏移量即跳过ID3类型的数据
    // 见2.1.1小节分析
    uint64_t offset = GetID3Size(&helper);

    // 此处代码处理为:从该系统属性值中获取文件解析器设置的标志位。其实就是对应于文件解析器格式类型值,
	// 若无设置则默认为0,其实就是将不会使用高通该模块功能。
	// 备注:默认在编译期间以及定义了该值的,一般情况下是启用所有支持的文件解析器格式类型值。
    property_get("vendor.mm.enable.qcom_parser", property_value, "0");
    parser_flags = atoi(property_value);
    LOGV("Parser_Flags == %x",parser_flags);
    
    // SNIFF_ARRAY_SIZE 一个宏定义,其值为支持的解复用文件格式数组大小,其为固定值
    // #define SNIFF_ARRAY_SIZE (sizeof(sniffArray)/sizeof(SniffInfo))
    // 关于 sniffArray 和 SniffInfo 的声明和定义,见2.1.2小节分析
    int index = 0;
    for (index = 0; index < SNIFF_ARRAY_SIZE; ++index) {
   
        const SniffInfo *sniff = &sniffArray[index];
    	// 与运算符作用为:检查启用了哪些支持的文件解析器格式类型值。即允许解析使用支持解析的哪些文件格式类型
    	// sniffArray:文件解析器格式支持列表数据,见上面的分析
        if (!(sniff->flag & parser_flags))
          continue;

        LOGV("Sniff %s ==>", sniff->type);
        // 此处的文件类型处理
        switch (sniff->format)
        {
   
            case FILE_SOURCE_AAC:
            case FILE_SOURCE_MP3:
            case FILE_SOURCE_FLAC:
            case FILE_SOURCE_APE:
            case FILE_SOURCE_DTS:
            case FILE_SOURCE_MHAS:
              // 这几种文件格式时,设置要求读取数据大小为最大值128KB
              requiredSize = MAX_FORMAT_HEADER_SIZE;
              break;
            default:
              // 否则,其他文件格式时默认设置要求读取数据大小为最小值1KB
              requiredSize = MIN_FORMAT_HEADER_SIZE;
              break;
        }

        //Check for file format and give validation a chance to request more data
        //if required buffer is not enough to sniff successfully. Maximum data that
        //can be requested is limited to max possible sniff buffer size.
        // 记录已读取数据字节数
        ssize_t dataRead = sniffBufferSize;
        do {
   
            //No need to read again until more data required
            if (!isEOS && requiredSize > dataRead) {
   
            	// 文件未到末尾并且还需要读取数据时
				
                // offset + dataRead:从offset数据偏移量即开始读取位置,
                // 加上已读取数据大小dataRead,即成为新的开始读取数据位置。
                // sniffBuffer + dataRead:计算本次需要存储读取到的数据的存储位置。
                // requiredSize - dataRead:计算还剩余需要读取的数据大小(字节)。
                ssize_t readBytes = helper.readAt(offset + dataRead,
                                                  sniffBuffer + dataRead,
                                     
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值