前言
上篇文章通过分析 MediaExtractorFactory::Create的UpdateExtractors函数我们清楚了不同格式媒体文件以及高通私有的extractor的so库注册过程,下面我们继续分MediaExtractorFactory::Create函数后续部分。首先还是先贴出函数代码:
66 sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
67 const sp<DataSource> &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<ExtractorPlugin> 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() : "<null>", 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<ExtractorPlugin> &plugin) {
133 *confidence = 0.0f;
134 *meta = nullptr;
135
136 std::shared_ptr<List<sp<ExtractorPlugin>>> 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<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(
MediaExtractor *extractor,
const sp<DataSource> &source,
const sp<RefBase> &plugin) {
if (extractor == nullptr) {
return nullptr;
}
return RemoteMediaExtractor::wrap(extractor, source, plugin);
}
//RemoteMediaExtractor.cpp
RemoteMediaExtractor::RemoteMediaExtractor(
MediaExtractor *extractor,
const sp<DataSource> &source,
const sp<RefBase> &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里面,方便后面使用。