承接上一章节分析: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,