Multimedia框架解析之MediaExtractor源码分析(二)

Multimedia框架解析之MediaExtractor源码分析(二),第1张

前言

上篇文章通过分析 MediaExtractorFactory::Create的UpdateExtractors函数我们清楚了不同格式媒体文件以及高通私有的extractor的so库注册过程,下面我们继续分MediaExtractorFactory::Create函数后续部分。首先还是先贴出函数代码:

66  sp MediaExtractorFactory::CreateFromService(
67          const sp &source, const char *mime) {
68  
69      ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
70  
71      UpdateExtractors(nullptr);
72  
73      // initialize source decryption if needed
74      source->DrmInitialization(nullptr /* mime */);
75  
76      void *meta = nullptr;
77      MediaExtractor::CreatorFunc creator = NULL;  //sniff返回后的CreatorFunc会保存到这里
78      MediaExtractor::FreeMetaFunc freeMeta = nullptr;
79      float confidence;
80      sp plugin;
81      creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin);
82      if (!creator) {
83          ALOGV("FAILED to autodetect media content.");
84          return NULL;
85      }
86  
87      MediaExtractor *ret = creator(source.get(), meta);
88      if (meta != nullptr && freeMeta != nullptr) {
89          freeMeta(meta);
90      }
91  
92      ALOGV("Created an extractor '%s' with confidence %.2f",
93           ret != nullptr ? ret->name() : "", confidence);
94  
95      return CreateIMediaExtractorFromMediaExtractor(ret, source, plugin);
96  }
MediaExtractorFactory::sniff

分析完UpdateExtractors之后,下面重要的函数就是sniff了。在MediaExtractorFactory::sniff中,会取出gPlugins保存的每一个extractor库,然后调用它们的sniff函数。上一篇我们已经分析过不同格式的多媒体文件已经注册了相应的Extractor,例如MP3Extractor中的sniff函数会去判断这个数据源是不是属于MP3格式的,如果是则会设置confidence为正数。MediaExtractorFactory::sniff会返回confidence值最大的那个extractor的CreatorFunc。CreatorFunc是每一个extractor 库都需要创建的一个static函数,用于构造extractor。

129 // static
130 MediaExtractor::CreatorFunc MediaExtractorFactory::sniff(
131         DataSourceBase *source, float *confidence, void **meta,
132         MediaExtractor::FreeMetaFunc *freeMeta, sp &plugin) {
133     *confidence = 0.0f;
134     *meta = nullptr;
135 
136     std::shared_ptr>> plugins;
137     {
138         Mutex::Autolock autoLock(gPluginMutex);
139         if (!gPluginsRegistered) {
140             return NULL;
141         }
142         plugins = gPlugins;
143     }
144 
145     MediaExtractor::CreatorFunc curCreator = NULL;
146     MediaExtractor::CreatorFunc bestCreator = NULL;
147     for (auto it = plugins->begin(); it != plugins->end(); ++it) {
148         float newConfidence;
149         void *newMeta = nullptr;
150         MediaExtractor::FreeMetaFunc newFreeMeta = nullptr;
151         if ((curCreator = (*it)->def.sniff(source, &newConfidence, &newMeta, &newFreeMeta))) {
152             if (newConfidence > *confidence) {
153                 *confidence = newConfidence;
154                 if (*meta != nullptr && *freeMeta != nullptr) {
155                     (*freeMeta)(*meta);
156                 }
157                 *meta = newMeta;
158                 *freeMeta = newFreeMeta;
159                 plugin = *it;
160                 bestCreator = curCreator;
161             } else {
162                 if (newMeta != nullptr && newFreeMeta != nullptr) {
163                     newFreeMeta(newMeta);
164                 }
165             }
166         }
167     }
168 
169     return bestCreator;
170 }

比如如果是mp3的source,则调用的是下面这个函数:


static MediaExtractor::CreatorFunc Sniff(
        DataSourceBase *source, float *confidence, void **meta,
        MediaExtractor::FreeMetaFunc *freeMeta) {
    off64_t pos = 0;
    off64_t post_id3_pos;
    uint32_t header;
    uint8_t mpeg_header[5];
    if (source->readAt(0, mpeg_header, sizeof(mpeg_header)) < (ssize_t)sizeof(mpeg_header)) {
        return NULL;
    }

    if (!memcmp("\x00\x00\x01\xba", mpeg_header, 4) && (mpeg_header[4] >> 4) == 2) {
        ALOGV("MPEG1PS container is not supported!");
        return NULL;
    }
    if (!Resync(source, 0, &pos, &post_id3_pos, &header)) {
        return NULL;
    }

    Mp3Meta *mp3Meta = new Mp3Meta;
    mp3Meta->pos = pos;
    mp3Meta->header = header;
    mp3Meta->post_id3_pos = post_id3_pos;
    *meta = mp3Meta;
    *freeMeta = ::free;

    *confidence = 0.2f;

    return CreateExtractor;
}

这里会去记录下当前MP3Extractor的confidence为0.2f,评分不怎么高啊!目前从我们的系统的log中可以看到,我们使用的都是高通的MMParserExtractor

 这是因为,高通的MMParserExtractor中我们将其confidence设置为0.8f,是比较高的一个值了。

高通sniff函数实现
// [vendor/qcom/proprietary/commonsys-intf/mm-parser/Android_adaptation/src/QComExtractorFactory.cpp]
static CreatorFunc Sniff(CDataSource *source, float *confidence,
                                         void **, FreeMetaFunc *) {

    DataSourceHelper helper(source);
    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;
    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))
    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;
}
sniffArray:文件解析器格式支持列表数据
// [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;
    // 这是个枚举类型,即高通该解复用模块支持的所有数据源文件格式类型,
    // 但是注意该枚举里面的类型并非所有的都支持了,只是将其所有的列出来而已。
    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】对象功能。
 

使用原生Extractor

暂时禁用高通的parser。

adb shell setprop vendor.mm.enable.qcom_parser 1

如果想要使用原生的Extractor,只需要将其confidence改成大于0.8f的浮点数即可!比如我这里,我将原生OGG格式的confidence值 改成了1.0f


static MediaExtractor::CreatorFunc Sniff(
        DataSourceBase *source,
        float *confidence,
        void **,
        MediaExtractor::FreeMetaFunc *) {
    char tmp[4];
    if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
        return NULL;
    }

    *confidence = 1.0f;

    return CreateExtractor;
}

编译后push相应的so库,得到的log如下:

12-20 14:22:02.984 V 839      1623     MediaExtractorFactory:  MediaExtractorFactory::CreateFromService (null) 
12-20 14:22:02.986 D 839      982      OggExtractor:           durationUs  188866 
12-20 14:22:02.986 D 839      1623     OggExtractor:           OggExtractor::OggExtractor!!!! 
12-20 14:22:02.987 V 839      982      MediaExtractorFactory:  Created an extractor OggExtractor with confidence 1.00 
12-20 14:22:02.987 D 839      1623     OggExtractor:           bps is 140000 
12-20 14:22:02.989 D 839      1623     OggExtractor:           durationUs  108145 

这时就用原生的OggExtractor,而不是高通的Extractor!

CreateIMediaExtractorFromMediaExtractor

sniff函数分析后,相当于已经准备好了各个格式最适合也就是评分最高的Extractor了,至于其他进程怎么去使用,就需要继续朝下看这个CreateIMediaExtractorFromMediaExtractor函数了。CreateIMediaExtractorFromMediaExtractor是InterfaceUtils.cpp里面的函数,这个函数把刚才创建的MediaExtractor,还有属于它的DataSource,ExtractorPlugin都传递给RemoteMediaExtractor,并构造RemoteMediaExtractor。因为MediaExtractor仅仅是一个本地类,无法提供跨进程访问接口,所以需要一个binder server为它提供接口,这个server就是RemoteMediaExtractor。来看看RemoteMediaExtractor的相关代码

sp CreateIMediaExtractorFromMediaExtractor(
        MediaExtractor *extractor,
        const sp &source,
        const sp &plugin) {
    if (extractor == nullptr) {
        return nullptr;
    }
    return RemoteMediaExtractor::wrap(extractor, source, plugin);
}



//RemoteMediaExtractor.cpp

RemoteMediaExtractor::RemoteMediaExtractor(
        MediaExtractor *extractor,
        const sp &source,
        const sp &plugin)
    :mExtractor(extractor),
     mSource(source),
     mExtractorPlugin(plugin) {
......
}

//RemoteMediaExtractor.h
 class RemoteMediaExtractor : public BnMediaExtractor { // IMediaExtractor wrapper to the MediaExtractor.
 }

这里可以看出RemoteMediaExtractor就是MediaExtractor的wrapper(封装),只要获得IMediaExtractor,就可以跨进程访问MediaExtractor,比如MP3Extractor。最后就是调用registerMediaExtractor把IMediaExtractor保存到ExtractorInstance里面,方便后面使用。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1499156.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-25
下一篇 2022-06-25

发表评论

登录后才能评论

评论列表(0条)

保存