一、概述
MediaExtractor的主要功能就是解封裝。它可以从各种媒体文件(如MP4、MP3、AAC、AV等)中提取出音频和视频的原始数据流,这个过程就是所谓的"解封装"。这些原始数据流可以直接被MedliaCodec进行编解码处理。 解封装的过程就是把包含在媒体文件中的音频和视频数据分离出来,以便于后续的处理和播放。这个过程并不涉及到编解码,所以通常来说,解封装的过程比编解码的过程要快很多。
MediaExtractor这个类主要负责打开媒体文件,井识别媒体文件的格式。它会根据媒体文件的格式,创建对应的Extractor (如MP4Extractor、 MPSExtractor等),井通过这个Extractor来读取媒体文件中的音频和视频数据。
MediaExtractor的解封装过程大致如下:
- 打开媒体文件:MediaExtractor首先通过openFd或setDataSource方法打开媒体文件。
- 识別媒体文件格式:MediaExtractor会读取媒体文件的头部信息,识别媒体文件的格式。
- 创建Extractor:根据媒体文件的格式,MediaExtractor会创建对应的Extractor 。
刚刚我们提到的setDataSource会调用nativeSetDataSource方法,我们可以发现他对应了jni层的frameworks/base/media/jni/android_media_MediaExtractor.cpp
里的android_media_MediaExtractor_setDataSource,参考源码我们在上述流程里的"2.识别媒体文件格式"和"3.创建Extractor"都是在这里执行的。
MediaExtractor内核心Api为:
- setDataSource(String path):设置数据源,可以是本地文件或者网络文件
- getTrackCount():得到源文件的轨道数
- getTrackFormat(int index): 获取指定(index)的轨道格式
- getSampleTime():返回当前的时间戳
- readSampleData(ByteBuffer byteBuf, int offset): 把指定轨道中的数据按偏移量读取到ByteBuffer中;
- advance():读取下一顿数据
- release():读取结束后释放资源
核心实现前置描述: MediaExtractor.cpp
的核心实现为NuMediaExtractor
NuMediaExtractor
的核心实现为sp<IMedlaExtractor>mImpL
,也就是IMediaExtractor
二、源码解析
1、初始化(实例化)
java层的MediaExtractor#mNativeContext
是什么时候被创建的: 在创建MediaExtractor
对象的时候,会调用native_setup()
,其对应的jn层的方法:android_media_MediaExtractor_native_setup
(jni层文件指路:frameworks/base/media/jni/android_media_MediaExtractor.cpp
),这个函数内部实现为:
cpp
static void android_media_MediaExtractor_native_setup(
JNIEnv *env, jobject thiz) {
sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
setMediaExtractor(env,thiz, extractor);
}
其实就创建了JMediaExtractor
对象并存到了MediaExtractor#mNativeContext
里面了,他也是jni层进行解封装的关键类。 而JMediaExtractor的实例化代码如下:
cpp
JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
: mClass(NULL),
mObject(NULL) {
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
mImpl = new NuMediaExtractor(NuMediaExtractor::EntryPoint::SDK);
}
2、setDataSource设置视频源
接下来我们来解析下jni层的android_media_MediaExtractor_setDataSource
的实现,其源码如下:
cpp
static void android_media_MediaExtractor_setDataSource(
JNIEnv *env, jobject thiz,
jobject httpServiceBinderObj,
jstring pathObj,
jobjectArray keysArray,
jobjectArray valuesArray) {
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
if (extractor == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
if (pathObj == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
KeyedVector<String8, String8> headers;
if (!ConvertKeyValueArraysToKeyedVector(
env, keysArray, valuesArray, &headers)) {
return;
}
const char *path = env->GetStringUTFChars(pathObj, NULL);
if (path == NULL) {
return;
}
sp<IMediaHTTPService> httpService;
if (httpServiceBinderObj != NULL) {
sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
httpService = interface_cast<IMediaHTTPService>(binder);
}
status_t err = extractor->setDataSource(httpService, path, &headers);
env->ReleaseStringUTFChars(pathObj, path);
path = NULL;
if (err != OK) {
jniThrowException(
env,
"java/io/IOException",
"Failed to instantiate extractor.");
return;
}
}
- 首先会调用
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz)
取出java层的MediaExtractor#NativeContext
成员变量,这个成员变量也在我们初始化的时候完成了创建。 - 紧接着会进行安全检查:检查
extractor
非空;检查视频源路经非空,如果出现问题会抛出异常。 - 在之后会把java层设置的参数,转换存到
KeyedVector<String8, String8> headers
里面。 - 之后则取出数据源的字符串。
- 然后会把Java对象
httpServiceBinderObj
转换为C++层的IMediaHTTPService
接口。 - 然后调用
extractor->setDataSource(httpService, path, &headers)
设置数据源 在extractor->setDataSource
设置数据源这一步,我们发现源码来到了android_media_MediaExtractor.cpp#JMediaExtractor::setDataSource
里,其执行的函数为mImpl->setDataSource(httpService, path, headers)
这里的mlmpl实例其实在创建JMediaExtractor里就已经创建,此时代码走到了frameworks/av/media/libstagefright/NuMediaExtractor.cpp#NuMediaExtractor::setDataSource
,其实现为
cpp
status_t NuMediaExtractor::setDataSource(
const sp<MediaHTTPService> &httpService,
const char *path,
const KeyedVector<String8, String8> *headers) {
Mutex::Autolock autoLock(mLock);
if (mImpl != NULL || path == NULL) {
return -EINVAL;
}
sp<DataSource> dataSource =
DataSourceFactory::getInstance()->CreateFromURI(httpService, path, headers);
if (dataSource == NULL) {
return -ENOENT;
}
// Initialize MediaExtractor using the data source
return initMediaExtractor(dataSource);
}
我们可以发现,其核心实现是:
- 创建DataSource对象:
sp<DataSource> dataSource = DataSourceFactory::getInstance()->CreateFromURI(httpService, path, headers);
- 初始化
NuMediaExtractor::initMediaExtractor(dataSource)
2.1创建DataSource对象
这里是使用工厂DataSourceFactory创建DataSouce,其源码如下:
cpp
sp<DataSource> DataSourceFactory::CreateFromURI(
const sp<MediaHTTPService> &httpService,
const char *uri,
const KeyedVector<String8, String8> *headers,
String8 *contentType,
HTTPBase *httpSource) {
if (contentType != NULL) {
*contentType = "";
}
sp<DataSource> source;
if (!strncasecmp("file://", uri, 7)) {
source = CreateFileSource(uri + 7);
} else if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {
if (httpService == NULL) {
ALOGE("Invalid http service!");
return NULL;
}
sp<HTTPBase> mediaHTTP = httpSource;
if (mediaHTTP == NULL) {
mediaHTTP = static_cast<HTTPBase *>(CreateMediaHTTP(httpService).get());
if (mediaHTTP == NULL) {
return NULL;
}
}
String8 cacheConfig;
bool disconnectAtHighwatermark = false;
KeyedVector<String8, String8> nonCacheSpecificHeaders;
if (headers != NULL) {
nonCacheSpecificHeaders = *headers;
NuCachedSource2::RemoveCacheSpecificHeaders(
&nonCacheSpecificHeaders,
&cacheConfig,
&disconnectAtHighwatermark);
}
if (mediaHTTP->connect(uri, &nonCacheSpecificHeaders) != OK) {
ALOGE("Failed to connect http source!");
return NULL;
}
if (contentType != NULL) {
*contentType = mediaHTTP->getMIMEType();
}
source = NuCachedSource2::Create(
mediaHTTP,
cacheConfig.empty() ? NULL : cacheConfig.c_str(),
disconnectAtHighwatermark);
} else if (!strncasecmp("data:", uri, 5)) {
source = DataURISource::Create(uri);
} else {
// Assume it's a filename.
source = CreateFileSource(uri);
}
if (source == NULL || source->initCheck() != OK) {
return NULL;
}
return source;
}
我们会发现其核心逻辑是根据文件url格式创建对应的DataSource。
2.2 初始化MediaExtrator
在2.1创建出DataSource对象以后,会调用initMediaExtractor(dataSource)
初始化MediaExtractor,其源码为:
cpp
status_t NuMediaExtractor::initMediaExtractor(const sp<DataSource>& dataSource) {
status_t err = OK;
mImpl = MediaExtractorFactory::Create(dataSource);
if (mImpl == NULL) {
ALOGE("%s: failed to create MediaExtractor", __FUNCTION__);
return ERROR_UNSUPPORTED;
}
setEntryPointToRemoteMediaExtractor();
if (!mCasToken.empty()) {
err = mImpl->setMediaCas(mCasToken);
if (err != OK) {
ALOGE("%s: failed to setMediaCas (%d)", __FUNCTION__, err);
return err;
}
}
// Get the name of the implementation.
mName = mImpl->name();
// Update the duration and bitrate
err = updateDurationAndBitrate();
if (err == OK) {
mDataSource = dataSource;
}
return OK;
}
对照上述代码,可以发现其流程为:创建新的MediaExtractor对象
首先是使用MediaExtractorFactory::Create(dataSource)
创建新的对象sp<IMediaExtractor> mImpl
cpp
sp<IMediaExtractor> MediaExtractorFactory::Create(
const sp<DataSource> &source, const char *mime) {
ALOGV("MediaExtractorFactory::Create %s", mime);
if (!property_get_bool("media.stagefright.extractremote", true)) {
// local extractor
ALOGW("creating media extractor in calling process");
return CreateFromService(source, mime);
} else {
// remote extractor
ALOGV("get service manager");
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
if (binder != 0) {
sp<IMediaExtractorService> mediaExService(
interface_cast<IMediaExtractorService>(binder));
sp<IMediaExtractor> ex;
mediaExService->makeExtractor(
CreateIDataSourceFromDataSource(source),
mime ? std::optional<std::string>(mime) : std::nullopt,
&ex);
return ex;
} else {
ALOGE("extractor service not running");
return NULL;
}
}
return NULL;
}
对照上述源码我们会发现,他会先检查是否有这个属性media.stagefright.extractremote
,如果有这个属性则执行CreateFromService(source, mime)
创建lMediaExtractor
对象。 media.stagefright.extractremote
这个属性就是用来控制媒体提取器是否运行在远程服务中的,如果这个属性为true,那么媒体提取器就会运行在单独的进程中;如果这个属性为talse,那么媒体提取器就会运行在主进程中。 在Android系统中,有一些操作是需要在单独的进程中进行的,以提高安全性和稳定性。例如,解析和提取媒体文件就是这样的操作。如果在解析媒体文件时发生了错误,例如文件格式不正确或者文件包含恶意代码,那么可能会导致媒体提取器崩渍。如果媒体提取播运行在主进程中,那么这种崩溃就可能导致整个应用崩溃。但如果媒体提取器运行在单独的进程中,那么就只有这个进程会崩溃,应用的其他部分可以继续运行。
3、CreatefromService创建单独进程的媒体解码器
接下来我们解析下CreateFromService(source, mime)
这个函数:
cpp
sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
const sp<DataSource> &source, const char *mime) {
ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
void *meta = nullptr;
void *creator = NULL;
FreeMetaFunc freeMeta = nullptr;
float confidence;
sp<ExtractorPlugin> plugin;
uint32_t creatorVersion = 0;
creator = sniff(source, &confidence, &meta, &freeMeta, plugin, &creatorVersion);
if (!creator) {
ALOGV("FAILED to autodetect media content.");
return NULL;
}
MediaExtractor *ex = nullptr;
if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1 ||
creatorVersion == EXTRACTORDEF_VERSION_NDK_V2) {
CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta);
if (meta != nullptr && freeMeta != nullptr) {
freeMeta(meta);
}
ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr;
}
ALOGV("Created an extractor '%s' with confidence %.2f",
ex != nullptr ? ex->name() : "<null>", confidence);
return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin);
}
对照上述代码我们会发现:
- 首先会执行sniff()函数来检测媒体数据的类型:
- sniff()函数会读取媒体数据的一部分,然后根据这些数据的内容和格式来列断媒体的类型。sniff函数的输出参数包括confidence值,一个meta数据,一个freeMeta通数,一个plugin插件和一个creatorVersion版本号。
- confidence值表示sniff()函数对检测结果的信心程度。如果confidence值很高,那么就说明sniff()函数很确定媒体的类型。meta数据包含了媒体的一些元信息,例如媒体的时长、分辨率等。freeMeta函数用于释放meta数据。plugin插件用于处理特定类型的媒体数据。creatorVersion版本号表示创建媒体提取器的版本。
- 然后,函数根据creatorVersion版本号来创建媒体提取器:
- 如果版本号是
EXTRACTORDEF_VERSION_NDK_V1
或者EXTRACTORDEF_VERSION_NDK_V2
,那么就使用creator酒数来创建 媒体提取器。creator函数的输入参数包括一个DataSource对象和一个meta数据, - 如果creator函数创建失败,则直接调用
new MediaExtractorCUnwrapper(ret)
创建媒体提取器对象
- 如果版本号是
- 最后执行
CreateIMediaExtractorFromMediaExtractor(ex, source, plugin)
,主要作用是将一个MediaExtractor
对象转换为一个IMediaExtractor
对象
对照上述流程我们会发现有几个关键点:sniff、creator、MediaExtractorCUnwrapper和CreateIMediaExtractorFromMediaExtractor我们一个个拆解
3.1 sniff函数
cpp
void *MediaExtractorFactory::sniff(
const sp<DataSource> &source, float *confidence, void **meta,
FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion) {
*confidence = 0.0f;
*meta = nullptr;
std::shared_ptr<std::list<sp<ExtractorPlugin>>> plugins;
{
Mutex::Autolock autoLock(gPluginMutex);
if (!gPluginsRegistered) {
return NULL;
}
plugins = gPlugins;
}
void *bestCreator = NULL;
for (auto it = plugins->begin(); it != plugins->end(); ++it) {
ALOGV("sniffing %s", (*it)->def.extractor_name);
float newConfidence;
void *newMeta = nullptr;
FreeMetaFunc newFreeMeta = nullptr;
void *curCreator = NULL;
if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) {
curCreator = (void*) (*it)->def.u.v2.sniff(
source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
} else if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
curCreator = (void*) (*it)->def.u.v3.sniff(
source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
}
if (curCreator) {
if (newConfidence > *confidence) {
*confidence = newConfidence;
if (*meta != nullptr && *freeMeta != nullptr) {
(*freeMeta)(*meta);
}
*meta = newMeta;
*freeMeta = newFreeMeta;
plugin = *it;
bestCreator = curCreator;
*creatorVersion = (*it)->def.def_version;
} else {
if (newMeta != nullptr && newFreeMeta != nullptr) {
newFreeMeta(newMeta);
}
}
}
}
return bestCreator;
}
对照上述源码,我们可以发现出现了std::list<sp<ExtractorPlugin>>
这个关键对象,ExtractorPlugin
是一个接口,它定义了一些用于处理特定类型的媒体数据的方法。例如,对于MP3格式的音频数据,就需要一个能够解析MP3格式的ExtractorPlugin
。 sniff
函数会遍历list<sp<ExtractorPlugin>>
对象,找到对应媒体文件的媒体提取器:
- 如果是
EXTRACTORDEF_VERSION_NDK_V1
版本,则执行(*it)->def.u.v2.sniff
- 如果是
EXTRACTORDEF_VERSION_NDK_V2
,则执行(*it)->def.u.v3.sniff
- 然后按最大的confidence,返回媒体提取器
bestCreator
那么是list<sp<ExtractorPlugin>
在什么时候创建的呢,答案是frameworks/av/media/libstagefright/MediaExtractorFactory.cpp#MediaExtractorFactory::LoadExtractors()
,其源码如下:
cpp
void MediaExtractorFactory::LoadExtractors() {
Mutex::Autolock autoLock(gPluginMutex);
if (gPluginsRegistered) {
return;
}
gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false);
std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
if (mediaNs != NULL) {
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = mediaNs,
};
RegisterExtractors("/apex/com.android.media/lib"
#ifdef __LP64__
"64"
#endif
"/extractors", &dlextinfo, *newList);
} else {
ALOGE("couldn't find media namespace.");
}
RegisterExtractors("/system/lib"
#ifdef __LP64__
"64"
#endif
"/extractors", NULL, *newList);
RegisterExtractors("/system_ext/lib"
#ifdef __LP64__
"64"
#endif
"/extractors", NULL, *newList);
newList->sort(compareFunc);
gPlugins = newList;
for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
for (size_t i = 0;; i++) {
const char* ext = (*it)->def.u.v3.supported_types[i];
if (ext == nullptr) {
break;
}
gSupportedExtensions.push_back(std::string(ext));
}
}
}
gPluginsRegistered = true;
}
对照上述代码,我们可以发现他会执行android_get_exported_namespace
获取com_android_media
这个命名空间。 Android的命名空间则是一种系统级的资源隔离机制。每个命名空间都有自己的资源集,这些资原可以是库(Library),也可以是其他类 型的资源。命名空间之间是隔离的,一个命名空间中的资源不能直接访问另一个命名空间中的资源。 然后就会执行RegisterExtractors
方法:
cpp
void MediaExtractorFactory::RegisterExtractors(
const char *libDirPath, const android_dlextinfo* dlextinfo,
std::list<sp<ExtractorPlugin>> &pluginList) {
ALOGV("search for plugins at %s", libDirPath);
DIR *libDir = opendir(libDirPath);
if (libDir) {
struct dirent* libEntry;
while ((libEntry = readdir(libDir))) {
if (libEntry->d_name[0] == '.') {
continue;
}
String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
if (!libPath.contains("extractor.so")) {
continue;
}
void *libHandle = android_dlopen_ext(
libPath.c_str(),
RTLD_NOW | RTLD_LOCAL, dlextinfo);
if (libHandle == nullptr) {
ALOGI("dlopen(%s) reported error %s", libPath.c_str(), strerror(errno));
continue;
}
GetExtractorDef getDef =
(GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
if (getDef == nullptr) {
ALOGI("no sniffer found in %s", libPath.c_str());
dlclose(libHandle);
continue;
}
ALOGV("registering sniffer for %s", libPath.c_str());
RegisterExtractor(
new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
}
closedir(libDir);
} else {
ALOGI("plugin directory not present (%s)", libDirPath);
}
}
我们可以发现主要作用是动态加载和注册媒体提取器插件,这样,当需要处理媒体数据时,就可以根据文件的格式选择合适的插件来处理数据,主要步骤为:
-
打开指定的库目录(libDirPath),这个目录中包含了一些动态链接库(.so文件)。
-
遍历这个目录中的每一个文件。如果文件名包含
extractor.so
,那么就尝试打开这个文件。 -
使用dlopen函数打开文件,获取到库的句柄(libHandle)。如果打开失败,那么就跳过这个文件,处理下一个文件。
-
使用dlsym函数从库中查找名为
GETEXTRACTORDEF
的符号。这个符号是一个函数,用于获取提取器的定义。 -
如果找到了这个符号,那么就创建一个新的
ExtractorPlugin
对象,然后将这个对象添加到插件列表(pluginList
) 中。cppExtractorPlugin(ExtractorDef definition, void *handle, String8 &path) : def(definition), libHandle(handle), libPath(path) { for (size_t i = 0; i < sizeof ExtractorDef::extractor_uuid; i++) { uuidString.appendFormat("%02x", def.extractor_uuid.b[i]); } }
我们可以发现其实就是把GETEXTRACTORDEF函数地址存到def里,库的句柄存到libHandle里、库路径存到libPath
-
如果遍历完所有的文件后,没有找到任何有效的插件。那么就输出一条日志,表示插件目录不存在。
LoadExtractors
会调用RegisterExtractors
循环执行上述流程,把
- "/apex/com.android.media/lib"
- "/system/lib"
- "/system_ext/lib"
这几个目录下的"extractor.so"都加载进来变成媒体提取器
3.2 creato函数
在刚才解析sniff()函数的时候,我们会发现creator函数赋值是在:
cpp
if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) {
curCreator = (void*) (*it)->def.u.v2.sniff(
source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
} else if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
curCreator = (void*) (*it)->def.u.v3.sniff(
source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
}
(*it)->def其实就是"extractor.so"里GETEXTRACTORDEF对应的函数,creator函数其实就是执行这个函数得到的返回函数
执行creator函数即可创建CMediaExtractor
函数
3.3 MediaExtractorCUnwrapper对象
MediaExtractorCUnwrapper对象其实就是包了下MediaExtractor
这个实例,然后创建的新对象
3.4 CreateIMediaExtractorFromMediaExtractor函数
arduino
sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(
MediaExtractor *extractor,
const sp<DataSource> &source,
const sp<RefBase> &plugin) {
if (extractor == nullptr) {
return nullptr;
}
return RemoteMediaExtractor::wrap(extractor, source, plugin);
}
观察源码可以发现其也是包了下MediaExtrator
至此android已经通过setDataSource拿到对应的媒体提取器了。
4、media.extrator主线程模式媒体提取器
我们先来回归下setDataSource是如何创建主线程模式媒体提取器,其源码:
cpp
// remote extractor
ALOGV("get service manager");
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
if (binder != 0) {
sp<IMediaExtractorService> mediaExService(
interface_cast<IMediaExtractorService>(binder));
sp<IMediaExtractor> ex;
mediaExService->makeExtractor(
CreateIDataSourceFromDataSource(source),
mime ? std::optional<std::string>(mime) : std::nullopt,
&ex);
return ex;
} else {
ALOGE("extractor service not running");
return NULL;
}
首先获取系统服务管理器并会尝试获取名为"media.extractor"的服务。这个服务其实就是"media.extractor"是媒体提取服务(Media Extractor Service)的名字。媒体提取服务是Android系统里提供的一种服务,它的主要功能就是从媒体文件中获取音频和视频的数据。
然后会通过类型转换赋值给sp<IMediaExtractorService> mediaExService
最后调用mediaExService->makeExtractor
赋值给sp<IMediaExtractor> ex
5、补充
我们在一开始的时候说MediaExtractor里有这几个核心的api:setDataSource、getTrackCount、getTrackFormat、getSampleTime、readSampleData、advance、release。这几个函数的核心实现其实都是调用我们刚才说的sp<IMediaExtractorService> mediaExService
至此MediaExtrator的源码解析部分就已经完成了,下面我们会展开讲讲MediaCodec了