概述
承接上一章节分析: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是如何进行模块化(集成)加载实现和注册过程的。
最后
以上就是无奈铅笔为你收集整理的Android底层音视频播放媒体提取器【MediaExtractor】的解复用模块demuxers模块化加载和注册流程实现源码分析【Part 2】的全部内容,希望文章能够帮你解决Android底层音视频播放媒体提取器【MediaExtractor】的解复用模块demuxers模块化加载和注册流程实现源码分析【Part 2】所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复