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

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

承接上一章节分析: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,
                                                  requiredSize - dataRead);
                // 返回值表示本次实际读取数据的大小,未读到数据则退出
                if (readBytes <= 0)
                    break;
                // 计算文件是否到末尾,也就是要求读取的数据大小,未读取到这么多数据
                // 【理论上数据足够的话,一次性就能读取完成,即此处相等】
                isEOS = ((requiredSize - dataRead) != readBytes);
                // 计算总读取数据大小
                dataRead += readBytes;
                ALOGV("Data read for sniffing at offset %llu required %u read %zu",
                      (unsigned long long)offset, requiredSize, dataRead);
            }

            // 检查最小要求读取数据是否满足
            //Check min sniff data is available
            if (dataRead < MIN_FORMAT_HEADER_SIZE) {
                LOGE("Sniff FAIL :: coundn't pull enough data for sniffing");
                // 数据不足时,将数组访问下标置为数组大小,即在下面的处理时,将会返回一个空结果给上层。
                index = SNIFF_ARRAY_SIZE;
                break;
            }

            // 将已读取数据字节数赋值给指定要求读取大小变量(修正)
            requiredSize = dataRead;
            // 检查文件格式,其实该方法是高通私有库【libmmparser_lite.so】实现的功能,因此目前分析不了,
            // 不过从其头文件的API英文注释可知,该方法就是将输入的sniffBuffer即携带文件格式数据进行文件格式是否支持检查,
            // 说是主要为了不不需要创建FileSource对象就能快速检查文件格式是否支持解复用。
            status = FileSource::CheckFileFormat(sniff->format, sniffBuffer, &requiredSize);
            LOGV("CheckFileFormat status = %d", status);
            // 若未完成读取数据,则继续
        }while (!isEOS && status == FILE_SOURCE_DATA_NOTAVAILABLE &&
                requiredSize <= MAX_FORMAT_HEADER_SIZE &&
                requiredSize > dataRead);

		// 记录已读取数据字节数
        sniffBufferSize = dataRead;
        if (status == FILE_SOURCE_SUCCESS) {
        	// 文件格式校验成功,则标记为true,并打分0.8百分比,最高1
            LOGV(" Sniff %s success <==", sniff->type);
            sniffSuccess = true;
            *confidence = 0.8;
            break;
        }
    }
    // 释放内存
    free(sniffBuffer);
    // 最后若成功,则返回该index对应的支持目标文件的SniffInfo配置信息对象的CreatorFunc方法指针
    return sniffSuccess ? sniffArray[index].creator : NULL;
}

2.1.1、GetID3Size(&helper)实现分析:
处理获取ID3数据的偏移量即跳过ID3类型的数据。
ID3定义:一种metadata容器,一般是位于一个mp3文件的开头或末尾的若干字节内,附加了关于该mp3的歌手,标题,专辑名称,年代,风格等信息,该信息就被称为ID3信息,ID3信息分为两个版本,v1和v2版。 其中:v1版的ID3在mp3文件的末尾128字节,以TAG三个字符开头,后面跟上歌曲信息。 v2版一般位于mp3的开头,可以存储歌词,该专辑的图片等大容量的信息。

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

#define ID3V2_HEADER_SIZE 10
/*! =========================================================================
 * @brief       Get ID3 tag size. This routine is used to skip ID3 tag data
 *              while sniffing.
 *
 * @param
 *   source[in] : Data source to read from.
 *
 * @return        ID3 tag size.
 * =========================================================================*/
uint64_t GetID3Size(DataSourceHelper *source)
{
    uint64_t offset = 0;
    // 循环检查文件开头是否有ID3 TAG标记消息存在
    // 注意:此处采取循环处理的原因是,ID3信息会有多个信息,因此需要循环读取全部出来并跳过
    //Check if file has ID3Tag present before AAC/MP3/FLAC/APE file header.
    for(;;) {
    	// 此处其实只处理ID3第二个版本的头信息,默认读取10个字节数据(头大小)
        uint8_t pProcessBuff[ID3V2_HEADER_SIZE];

        // 读取至少10个字节数据,调用了readAt方法
        // 其方法返回值表示读取到的数据大小,若小于10个字节则直接退出,表示该文件没数据了
        //read atleast 10 bytes of data
        if(source->readAt(offset,pProcessBuff,sizeof(pProcessBuff))
           < (ssize_t)sizeof(pProcessBuff)) {
            break;
        }

        // 比较两个字符串内存字节的前三个字符的(三)字节大小,返回0表示相同,返回不相同时退出
        //Stop if there are no more ID3 tags
        if(memcmp("ID3",pProcessBuff,strlen("ID3")))
            break;

        // 计算ID3类型数据大小并跳过它
        //Calculate size and skip ID3 tag
        // 跳过六个字节大小数据即 ID3 (三字符大小) + Ver (版本号两字节大小) + Flag (标记一字节大小) 
        //skip 6 byte = ID3(3byte)+ Ver(2byte)+ Flag (1byte)
        // 从文件数据开头的第6个字节之后的数据开始计算ID3 tag 大小,计算到第10个字节处
        // 注意:该size数据类型是64位bit长度的无符号类型
        uint64_t ulID3TagSize = 0;
        for(int i = 6; i < ID3V2_HEADER_SIZE; i++) {
        	// (ulID3TagSize << 7) 表示 size的值做左移运算符计算 
        	// 即size值的bit数位全部左移7个位,也就是将最高位的7个bit值移除。
        	// (pProcessBuff[i] & 0x7F) 表示 将 pProcessBuff 字节数组中的第7到10的字节数值和 0x7F【0111 1111】进行与运算符计算,
        	// 也就说将 pProcessBuff[i] 第i个字节数据的bit最高位去掉(置为0)。
        	// 最后将这两个计算得到的值继续 | 或运算符,其实际效果和作用根据这两个计算处理流程中分析的可知,
        	// 最终作用就是将 pProcessBuff[i] 后面四个字节的右7位有效bit值加最高位0,按顺序排列成一个64位int变量的右32位来表示的值。
            ulID3TagSize = (ulID3TagSize << 7) | (pProcessBuff[i] & 0x7F);
        }
        // 可能此时你还看不出来到底这么处理后得到的值表示啥,其实就是表示当前ID3 tag后面携带的信息长度值,
        // 因此如此计算后面4个字节有效bit位,得到的size值就表示该长度值

		// 因此此处就在加上ID3头部信息大小(10字节),得到的总值就是当前ID3 tag及其携带信息的总长度字节数
        ulID3TagSize += ID3V2_HEADER_SIZE;

		// 然后再次相加此前已计算的ID3 tag信息长度,就得到所有ID3 tag及其携带信息的总长度字节数
		// 而该值就是需要跳过的值,跳过该值后就是文件头即文件类型信息了。
        offset += ulID3TagSize;
    }
    return offset;
}

2.1.2、sniffArray:文件解析器格式支持列表数据
其实为一个全局固定数据大小的数组。
以下文件均在【QComExtractorFactory.cpp】文件中实现

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

#define CREATOR(_x_) [](CDataSource *source, void *) -> CMediaExtractor* { \
  return wrap(new MMParserExtractor(new DataSourceHelper(source), _x_)); \
}

#define SNIFF_ENTRY(_flag_, _type_, _format_) {_flag_, _type_, _format_, CREATOR(_format_)}

typedef struct
{
    int flag;
    const char* type;
    // 这是个枚举类型,即高通该解复用模块支持的所有数据源文件格式类型,
    // 但是注意该枚举里面的类型并非所有的都支持了,只是将其所有的列出来而已。
    // 想要知道具体哪些文件格式类型支持情况,可以查看2.2小节中的分析
    FileSourceFileFormat format;
    CreatorFunc creator;
} SniffInfo;

static const SniffInfo sniffArray[] = {
  SNIFF_ENTRY(PARSER_OGG,    "OGG",    FILE_SOURCE_OGG),
  SNIFF_ENTRY(PARSER_AVI,    "AVI",    FILE_SOURCE_AVI),
  SNIFF_ENTRY(PARSER_WAV,    "WAV",    FILE_SOURCE_WAV),
  SNIFF_ENTRY(PARSER_DSF,    "DSF",    FILE_SOURCE_DSF),
  SNIFF_ENTRY(PARSER_DSDIFF, "DSDIFF", FILE_SOURCE_DSDIFF),
  SNIFF_ENTRY(PARSER_AC3,    "AC3",    FILE_SOURCE_AC3),
  SNIFF_ENTRY(PARSER_ASF,    "ASF",    FILE_SOURCE_ASF),
  SNIFF_ENTRY(PARSER_AMR_NB, "AMRNB",  FILE_SOURCE_AMR_NB),
  SNIFF_ENTRY(PARSER_AMR_WB, "AMRWB",  FILE_SOURCE_AMR_WB),
  SNIFF_ENTRY(PARSER_MKV,    "MKV",    FILE_SOURCE_MKV),
  SNIFF_ENTRY(PARSER_MOV,    "MOV",    FILE_SOURCE_MOV),
  SNIFF_ENTRY(PARSER_3GP,    "3GP",    FILE_SOURCE_MPEG4),
  SNIFF_ENTRY(PARSER_QCP,    "QCP",    FILE_SOURCE_QCP),
  SNIFF_ENTRY(PARSER_FLAC,   "FLAC",   FILE_SOURCE_FLAC),
  SNIFF_ENTRY(PARSER_FLV,    "FLV",    FILE_SOURCE_FLV),
  SNIFF_ENTRY(PARSER_MP2TS,  "MP2TS",  FILE_SOURCE_MP2TS),
  SNIFF_ENTRY(PARSER_3G2,    "3G2",    FILE_SOURCE_3G2),
  SNIFF_ENTRY(PARSER_MP2PS,  "MP2PS",  FILE_SOURCE_MP2PS),
  SNIFF_ENTRY(PARSER_AIFF,   "AIFF",   FILE_SOURCE_AIFF),
  SNIFF_ENTRY(PARSER_APE,    "APE",    FILE_SOURCE_APE),
  SNIFF_ENTRY(PARSER_AAC,    "AAC",    FILE_SOURCE_AAC),
  SNIFF_ENTRY(PARSER_MP3,    "MP3",    FILE_SOURCE_MP3),
  SNIFF_ENTRY(PARSER_DTS,    "DTS",    FILE_SOURCE_DTS),
  SNIFF_ENTRY(PARSER_MHAS,   "MHAS",   FILE_SOURCE_MHAS)
};

通过上面的分析可以知道,sniffArray该数组将会定义所有当前高通解复用模块支持的文件格式数据信息SniffInfo,并通过宏定义方法【CREATOR】赋值来创建具体的真正的解复用模块媒体提取器实现对象指针【CMediaExtractor*】,并且通过一个wrap方法来对【MMParserExtractor】对象进行转换,其实就是相当于对数据对象功能的封装,即代理设计模式,上层使用CMediaExtractor对象来代理操作真正的实现者【MMParserExtractor】对象功能。
具体是怎么实现的,可以简单如下分析:
先看类声明:【省略其他代码】

// [vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/inc/MMParserExtractor.h]
class MMParserExtractor : public MediaExtractorPluginHelper {}

// [frameworks/av/include/media/MediaExtractorPluginHelper.h]
// extractor plugins can derive from this class which looks remarkably
// like MediaExtractor and can be easily wrapped in the required C API
class MediaExtractorPluginHelper {}

wrap()方法肯定不会凭空出现,因此QComExtractorFactory.cpp该文件中没有定义,那么肯定是在头文件中声明定义的,
其只引用了一个头文件:

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

#include "MMParserExtractor.h"

因此再看该头文件中引用的头文件:

// [vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/inc/MMParserExtractor.h]

#define MMPARSER_EXTRACTOR_H_
#include <utils/threads.h>
#include <cutils/properties.h>
#include <media/MediaExtractorPluginHelper.h>
#include <media/NdkMediaFormat.h>
#include "SourcePort.h"
#include "filesource.h"
#include "filesourcetypes.h"
#include "common_log.h"

其实可以大致猜到该方法肯定跟前面内容相关,因此在MediaExtractorPluginHelper.h文件中可以看到该wrap()方法的声明定义:
因此方法的实现就完成了代理类功能的实现,即代理设计模式

// [frameworks/av/include/media/MediaExtractorPluginHelper.h]

inline CMediaExtractor *wrap(MediaExtractorPluginHelper *extractor) {
	// 创建一个【CMediaExtractor】代理类
    CMediaExtractor *wrapper = (CMediaExtractor*) malloc(sizeof(CMediaExtractor));
    wrapper->data = extractor;
    wrapper->free = [](void *data) -> void {
        delete (MediaExtractorPluginHelper*)(data);
    };
    wrapper->countTracks = [](void *data) -> size_t {
        return ((MediaExtractorPluginHelper*)data)->countTracks();
    };
    wrapper->getTrack = [](void *data, size_t index) -> CMediaTrack* {
    	// 注意:此处也是使用了代理设计模式,将底层的Track对象封装【转换】为上层的Track对象,以供上层控制下层实现
    	// 见下面的分析
        return wrap(((MediaExtractorPluginHelper*)data)->getTrack(index));
    };
    wrapper->getTrackMetaData = [](
            void *data,
            AMediaFormat *meta,
            size_t index, uint32_t flags) -> media_status_t {
        return ((MediaExtractorPluginHelper*)data)->getTrackMetaData(meta, index, flags);
    };
    wrapper->getMetaData = [](
            void *data,
            AMediaFormat *meta) -> media_status_t {
        return ((MediaExtractorPluginHelper*)data)->getMetaData(meta);
    };
    wrapper->flags = [](
            void *data) -> uint32_t {
        return ((MediaExtractorPluginHelper*)data)->flags();
    };
    wrapper->setMediaCas = [](
            void *data, const uint8_t *casToken, size_t size) -> media_status_t {
        return ((MediaExtractorPluginHelper*)data)->setMediaCas(casToken, size);
    };
    wrapper->name = [](
            void *data) -> const char * {
        return ((MediaExtractorPluginHelper*)data)->name();
    };
    return wrapper;
}

wrap(((MediaExtractorPluginHelper*)data)->getTrack(index))实现分析:
也是使用了代理设计模式,将底层的Track对象封装【转换】为上层的Track对象,以供上层控制下层实现。
首先看下MediaExtractorPluginHelper的getTrack() 方法返回类型:

// [frameworks/av/include/media/MediaExtractorPluginHelper.h]

    virtual MediaTrackHelper *getTrack(size_t index) = 0;

wrap()实现:

// [frameworks/av/include/media/MediaExtractorPluginHelper.h]

inline CMediaTrack *wrap(MediaTrackHelper *track) {
    if (track == nullptr) {
        return nullptr;
    }
    // 创建Track代理类
    CMediaTrack *wrapper = (CMediaTrack*) malloc(sizeof(CMediaTrack));
    wrapper->data = track;
    wrapper->free = [](void *data) -> void {
        delete (MediaTrackHelper*)(data);
    };
    // 开始Track,代理实现
    wrapper->start = [](void *data, CMediaBufferGroup *bufferGroup) -> media_status_t {
        if (((MediaTrackHelper*)data)->mBufferGroup) {
            // this shouldn't happen, but handle it anyway
            delete ((MediaTrackHelper*)data)->mBufferGroup;
        }
        // 每次开始track时都会新分配缓冲区封装对象给到下层使用,
        // 也就是说媒体解复用缓冲区内存是由上层调用端来统一分配的。
        ((MediaTrackHelper*)data)->mBufferGroup = new MediaBufferGroupHelper(bufferGroup);
        return ((MediaTrackHelper*)data)->start();
    };
    // 停止track
    wrapper->stop = [](void *data) -> media_status_t {
        return ((MediaTrackHelper*)data)->stop();
    };
    // 获取track到的媒体信息
    wrapper->getFormat = [](void *data, AMediaFormat *meta) -> media_status_t {
        return ((MediaTrackHelper*)data)->getFormat(meta);
    };
    // track解复用读取解析的数据,传入读取操作的参数
    wrapper->read = [](void *data, CMediaBuffer **buffer,  uint32_t options, int64_t seekPosUs)
            -> media_status_t {
        MediaTrackHelper::ReadOptions opts(options, seekPosUs);
        MediaBufferHelper *buf = NULL;
        // 调用底层Track实现的read()方法来真正解复用获取音视频数据
        media_status_t ret = ((MediaTrackHelper*)data)->read(&buf, &opts);
        if (ret == AMEDIA_OK && buf != nullptr) {
        	// 成功则获取该buffer数据
            *buffer = buf->mBuffer;
        }
        return ret;
    };
    // 是否支持非阻塞读取
    wrapper->supportsNonBlockingRead = [](void *data) -> bool {
                return ((MediaTrackHelper*)data)->supportsNonBlockingRead();
    };
    return wrapper;
}

2.2、GetExtensions()实现分析:
获取该媒体提取器so库支持的文件扩展类型,返回类型为char ** 其实也就是字符串数组,即返回的是支持解复用的文件格式类型名称数组。

// [vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/src/QComExtractorFactory.cpp]
static const char** GetExtensions() {
	// 此处代码处理为:从该系统属性值中获取文件解析器设置的标志位。其实就是对应于文件解析器格式类型值,
	// 若无设置则默认为0,其实就是将不会使用高通该模块功能。
	// 备注:默认在编译期间以及定义了该值的,一般情况下是启用所有支持的文件解析器格式类型值。
    int  parser_flags = 0;
    char property_value[PROPERTY_VALUE_MAX] = {0};
    property_get("vendor.mm.enable.qcom_parser", property_value, "0");
    parser_flags = atoi(property_value);
    LOGV("Parser_Flags == %x",parser_flags);
    
    int index = 0;
    total_extensions = 0;
    // 关于 sniffArray 和 SniffInfo 的声明和定义,见上面的分析
    for (index = 0; index < SNIFF_ARRAY_SIZE; ++index) {
    	// 与运算符作用为:检查启用了哪些支持的文件解析器格式类型值。即允许解析使用支持解析的哪些文件格式类型
    	// sniffArray:文件解析器格式支持列表数据,见上面的分析
        switch((parser_flags & sniffArray[index].flag)) {
            case PARSER_OGG:
            	// 支持该文件格式和它的扩展文件格式,也就是说一种类型文件可能有多种文件格式来表示
            	// 见下面的分析
                addExtension("oga");
                addExtension("ogg");
                break;
            case PARSER_AVI:
                addExtension("avi");
                break;
            case PARSER_WAV:
                addExtension("wav");
                break;
            case PARSER_DSF:
                addExtension("dsf");
                break;
            case PARSER_DSDIFF:
                addExtension("dff");
                addExtension("dsd");
                break;
            case PARSER_AC3:
                addExtension("ac3");
                addExtension("ec3");
                break;
            case PARSER_ASF:
                addExtension("asf");
                addExtension("wma");
                addExtension("wmv");
                break;
            case PARSER_AMR_NB:
                addExtension("amr");
                break;
            case PARSER_AMR_WB:
                addExtension("awb");
                break;
            case PARSER_MKV:
                addExtension("mka");
                addExtension("mkv");
                addExtension("webm");
                break;
            case PARSER_MOV:
                addExtension("mov");
                break;
            case PARSER_3GP:
                addExtension("3gp");
                addExtension("3gpp");
                addExtension("3gpp2");
                addExtension("m4a");
                addExtension("m4r");
                addExtension("m4v");
                addExtension("mp4");
                addExtension("qt");
                break;
            case PARSER_QCP:
                addExtension("qcp");
                break;
            case PARSER_FLAC:
                addExtension("fl");
                addExtension("flac");
                break;
            case PARSER_FLV:
                addExtension("flv");
                break;
            case PARSER_MP2TS:
                addExtension("m2ts");
                addExtension("ts");
                break;
            case PARSER_3G2:
                addExtension("3g2");
                break;
            case PARSER_MP2PS:
                addExtension("m2p");
                addExtension("mpeg");
                addExtension("mpg");
                addExtension("mpga");
                addExtension("vob");
                break;
            case PARSER_AIFF:
                addExtension("aif");
                addExtension("aiff");
                break;
            case PARSER_APE:
                addExtension("ape");
                break;
            case PARSER_AAC:
                addExtension("aac");
                break;
            case PARSER_MP3:
                addExtension("mp3");
                break;
            case PARSER_DTS:
                addExtension("dts");
                break;
            case PARSER_MHAS:
                addExtension("mhas");
                break;
            default:
                break;
        }
    }
    // 最后加了一个NULL空数据项到数组最后项中
    addExtension(NULL);
    return extensions;
}

addExtension(“ogg”)实现分析:

// [vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/src/QComExtractorFactory.cpp]
static void addExtension(const char* extension)
{
    if (total_extensions < MAX_FILE_EXTENSIONS) {
        extensions[total_extensions++] = extension;
    }
}

extensions 数组指针全局变量定义:
[vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/src/QComExtractorFactory.cpp]文件中

static const char *extensions[MAX_FILE_EXTENSIONS];

// 最终支持50个文件格式类型
#define MAX_FILE_EXTENSIONS 50

因此从上面的 GetExtensions() 方法分析可以知道,这里定义了高通私有解复用模块支持的文件类型格式情况。

本章结束:
通过本系列章节的分析,我们可以清晰的理解和掌握,Android底层音视频播放媒体提取器【MediaExtractor】的解复用模块demuxers是如何进行模块化(集成)加载实现和注册过程的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值