承接上一章节分析:【五】Android MediaPlayer整体架构源码分析 -【prepareAsync/prepare数据准备处理流程】【Part 1】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
【此章节小节编号就接着上一章节排列】
3.2、CreateDataSourceFromIDataSource(source)实现分析:
将IDataSource(BpBinder)类型数据对象封装成客户端需要的代理类对象DataSource类型
// [frameworks/av/media/libstagefright/InterfaceUtils.cpp]
sp<DataSource> CreateDataSourceFromIDataSource(const sp<IDataSource> &source) {
if (source == nullptr) {
return nullptr;
}
// 进过两次封装类处理,即都是代理类实现
// 见下面的分析
return new TinyCacheSource(new CallbackDataSource(source));
}
CallbackDataSource类声明:
// [frameworks/av/media/libmedia/include/CallbackDataSource.h]
// A stagefright DataSource that wraps a binder IDataSource. It's a "Callback"
// DataSource because it calls back to the IDataSource for data.
class CallbackDataSource : public DataSource {}
CallbackDataSource类构造函数定义:
也就是代理类实现的代理功能
// [frameworks/av/media/libmedia/CallbackDataSource.cpp]
CallbackDataSource::CallbackDataSource(
const sp<IDataSource>& binderDataSource)
: mIDataSource(binderDataSource),
mIsClosed(false) {
// Set up the buffer to read into.
mMemory = mIDataSource->getIMemory();
mName = String8::format("CallbackDataSource(%d->%d, %s)",
// 当前进程ID
getpid(),
// 当前调用者进程ID(如上层APP的进程号)
IPCThreadState::self()->getCallingPid(),
mIDataSource->toString().string());
}
TinyCacheSource类声明:
// [frameworks/av/media/libmedia/include/CallbackDataSource.h]
// A caching DataSource that wraps a CallbackDataSource. For reads smaller
// than kCacheSize it will read up to kCacheSize ahead and cache it.
// This reduces the number of binder round trips to the IDataSource and has a significant
// impact on time taken for filetype sniffing and metadata extraction.
class TinyCacheSource : public DataSource {}
TinyCacheSource 类构造函数定义:
// [frameworks/av/media/libmedia/CallbackDataSource.cpp]
TinyCacheSource::TinyCacheSource(const sp<DataSource>& source)
: mSource(source), mCachedOffset(0), mCachedSize(0) {
mName = String8::format("TinyCacheSource(%s)", mSource->toString().string());
}
3.3、notifyPreparedAndCleanup(UNKNOWN_ERROR)实现分析:
向上层应用通知prepare流程完成并上报该错误
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
if (err != OK) {
// 若不成功,则清除工作
{
Mutex::Autolock _l_d(mDisconnectLock);
mDataSource.clear();
mHttpSource.clear();
}
mCachedSource.clear();
mBitrate = -1;
mPrevBufferPercentage = -1;
// 拉取缓冲数据代数值加1,即会打断此时可能正要执行获取buffer数据的处理
++mPollBufferingGeneration;
}
// 通知prepare完成并携带完成状态
notifyPrepared(err);
}
notifyPrepared(err)实现分析:
其实注意,该方法其实不是在GenericSource.cpp中实现的,而是在NuPlayer.cpp中实现的。
备注:关于ALooper消息循环系统实现原理请查看另外章节:Android native层媒体通信架构AHandler/ALooper机制实现源码分析
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::Source::notifyPrepared(status_t err) {
ALOGV("Source::notifyPrepared %d", err);
// dupNotify() 复制一个空闲的事件通知消息对象
// 见下面的分析
sp<AMessage> notify = dupNotify();
notify->setInt32("what", kWhatPrepared);
notify->setInt32("err", err);
// 携带两个参数后立即发送该消息给消息接收者AHandler
notify->post();
}
dupNotify() 复制一个该事件通知消息对象:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerSource.h]
struct NuPlayer::Source : public AHandler {
// The provides message is used to notify the player about various
// events.
explicit Source(const sp<AMessage> ¬ify)
: mNotify(notify) {
}
sp<AMessage> dupNotify() const { return mNotify->dup(); }
}
结合前面章节的分析,可知mNotify通知事件消息对象其实是在NuPlayer的setDataSource流程中创建GenericSource时进行赋值的。即该消息对象定义实现如下:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
发送该消息给NuPlayer的消息循环系统接收,因此处理如下:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSourceNotify:
{
onSourceNotify(msg);
break;
}
}
}
onSourceNotify(msg)实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onSourceNotify(const sp<AMessage> &msg) {
// 取出事件参数
int32_t what;
CHECK(msg->findInt32("what", &what));
switch (what) {
case Source::kWhatPrepared:
{
ALOGV("NuPlayer::onSourceNotify Source::kWhatPrepared source: %p", mSource.get());
// 调用reset()后mSource被置为null,因此不再需要上报本次多余(旧)通知
if (mSource == NULL) {
// This is a stale notification from a source that was
// asynchronously preparing when the client called reset().
// We handled the reset, the source is gone.
break;
}
// 取出错误状态码
int32_t err;
CHECK(msg->findInt32("err", &err));
if (err != OK) {
// 不成功时
// 此时需要关闭可能存在的加密解码器,如当前客户端没调用reset时候
// shut down potential secure codecs in case client never calls reset
mDeferredActions.push_back(
new FlushDecoderAction(FLUSH_CMD_SHUTDOWN /* audio */,
FLUSH_CMD_SHUTDOWN /* video */));
// 然后执行上面的操作
processDeferredActions();
// shutdown流程操作就会在后续reset流程分析章节中出现,此处不展开分析
} else {
// 成功,则标记完成prepare标志
mPrepared = true;
}
sp<NuPlayerDriver> driver = mDriver.promote();
if (driver != NULL) {
// notify duration first, so that it's definitely set when
// the app received the "prepare complete" callback.
int64_t durationUs;
// GenericSource中该方法其实始终返回OK,但获取的该值在prepare失败的情况下将只能获取到初始化值即-1
if (mSource->getDuration(&durationUs) == OK) {
// 见下面的分析
driver->notifyDuration(durationUs);
}
// 通知prepare完成
// 见下面的分析
driver->notifyPrepareCompleted(err);
}
break;
}
}
}
driver->notifyDuration()实现分析:
其实就是缓存当前文件媒体时长值而已
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
void NuPlayerDriver::notifyDuration(int64_t durationUs) {
Mutex::Autolock autoLock(mLock);
mDurationUs = durationUs;
}
driver->notifyPrepareCompleted(err)实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
ALOGV("notifyPrepareCompleted %d", err);
Mutex::Autolock autoLock(mLock);
// 检查此时NuPlayerDriver类对象中的状态是否还停留在 正在prepare流程中 状态。
// 若不是的话,则将忽略本次通知,因为client端已改变了播放器行为。该通知已过期。
if (mState != STATE_PREPARING) {
// We were preparing asynchronously when the client called
// reset(), we sent a premature "prepared" notification and
// then initiated the reset. This notification is stale.
CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
return;
}
CHECK_EQ(mState, STATE_PREPARING);
// 记录prepare执行的状态值
mAsyncResult = err;
if (err == OK) {
// prepare成功时立即扭转状态为 prepare已完成状态
// update state before notifying client, so that if client calls back into NuPlayerDriver
// in response, NuPlayerDriver has the right state
mState = STATE_PREPARED;
// 若是异步执行prepare处理,则执行通知上层APP该prepare结果状态值
// 备注:notifyListener_l() 该方法之后的调用流程不再分析了,可参考前面章节中关于Listener通知机制的分析。
if (mIsAsyncPrepare) {
notifyListener_l(MEDIA_PREPARED);
}
} else {
// 失败时则返回上一个状态即setDataSource完成状态,并通知上层APP该错误事件
mState = STATE_UNPREPARED;
if (mIsAsyncPrepare) {
notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
}
}
// 获取文件元数据信息,若prepare失败则返回null
sp<MetaData> meta = mPlayer->getFileMeta();
int32_t loop;
if (meta != NULL
&& meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
// kKeyAutoLoop该TAG元数据值标识:Ogg文件可以被标记为自动循环播放模式。
// 因此在android原生播放器播放ogg文件时可能会是单循环播放。可参考前面章节关于该字段的分析
mAutoLoop = true;
}
// 唤醒可能在该条件变量上等待的所有线程【如同步执行prepare流程时就将会阻塞调用线程,等待prepare完成】
mCondition.broadcast();
}
3.4、initFromDataSource()实现分析:
从DataSource数据源中初始化媒体提取器,即加载初始化具体的媒体解复用模块。
推荐看下此章节内容分析:
【关于android底层解复用模块插件模块化实现原理请查看:Android底层音视频播放媒体提取器【MediaExtractor】的解复用模块demuxers模块化加载和注册流程实现源码分析】
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
status_t NuPlayer::GenericSource::initFromDataSource() {
sp<IMediaExtractor> extractor;
sp<DataSource> dataSource;
// 加锁代码块来获取全局DataSource数据源数据访问对象
{
Mutex::Autolock _l_d(mDisconnectLock);
dataSource = mDataSource;
}
// 必须不能为空
CHECK(dataSource != NULL);
// 注意:下面流程中的先解锁后加锁操作的原因:
// 其实就是在prepare事件处理时GenericSource在调用 onPrepareAsync() 方法前已使用了自动锁来加锁处理,
// 因此必须在我们这里不需要加锁的地方先解锁,但一定要记得方法返回后必须加锁,然后让自动锁来释放最终的锁。
// 解锁
mLock.unlock();
// 创建工厂来创建媒体提取器,不需要加锁
// 备注:可能会耗时
// 见3.4.1小节分析
// This might take long time if data source is not reliable.
extractor = MediaExtractorFactory::Create(dataSource, NULL);
if (extractor == NULL) {
ALOGE("initFromDataSource, cannot create extractor!");
mLock.lock();
return UNKNOWN_ERROR;
}
// 获取文件元数据项信息,其实就是KV值数据项
// 备注:通过前面MediaExtractor模块化加载实现原理可知道,
// 调用getMetaData()其实就是调用具体提取器实现者的该方法,
// 比如前面相关章节分析的高通私有库解复用模块【mm-parser】实现的 MMParserExtractor 类,
// 将会调用该类实现的getMetaData()方法。
// 关于具体解复用模块的具体实现分析后续有时间再分析吧。
sp<MetaData> fileMeta = extractor->getMetaData();
// 同上,调用具体提取器实现类的该方法,获取媒体中的Track个数
// 【同一个文件中可能有多个音频流或多个视频流,因此该值可能会大于2】
size_t numtracks = extractor->countTracks();
if (numtracks == 0) {
ALOGE("initFromDataSource, source has no track!");
mLock.lock();
return UNKNOWN_ERROR;
}
mLock.lock();
// 缓存文件媒体元数据项对象
mFileMeta = fileMeta;
if (mFileMeta != NULL) {
// 获取该元数据信息对象中媒体时长关键字段对应的媒体时长值,并缓存
int64_t duration;
if (mFileMeta->findInt64(kKeyDuration, &duration)) {
mDurationUs = duration;
}
}
// 总码率 bps
int32_t totalBitrate = 0;
mMimes.clear();
for (size_t i = 0; i < numtracks; ++i) {
// 同上分析,调用具体提取器实现类的该方法,获取媒体中第i个位置的Track对象信息
sp<IMediaSource> track = extractor->getTrack(i);
if (track == NULL) {
// 若为空继续下一个track获取
continue;
}
// 调用具体提取器实现类的该方法,
// 获取媒体中第i个位置的Track对象信息的媒体元数据信息对象。
sp<MetaData> meta = extractor->getTrackMetaData(i);
if (meta == NULL) {
ALOGE("no metadata for track %zu", i);
return UNKNOWN_ERROR;
}
// 获取该元数据信息中kKeyMIMEType字段的文件类型值
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
ALOGV("initFromDataSource track[%zu]: %s", i, mime);
// Do the string compare immediately with "mime",
// we can't assume "mime" would stay valid after another
// extractor operation, some extractors might modify meta
// during getTrack() and make it invalid.
// 必须立即检查mime文件类型有效性
// 原因是底层提取器具体实现者可能会修改了该track信息并使其无效
if (!strncasecmp(mime, "audio/", 6)) {
// audio文件格式类型时
// 音频track信息对象初始化处理
// 注意:此处处理只会创建一次track对象,因此若当前媒体文件有多个音频流时,
// 则只会默认处理第一个track信息,而第一个track信息不一定真的能够被解码器解码,
// 若解码器解码失败的话,那么将会在解码器解码两帧数据失败后shutdown解码器操作。
// 若此时是视频文件那么将会独自播放视频流而没有声音。
if (mAudioTrack.mSource == NULL) {
// 记录track索引和track对象
mAudioTrack.mIndex = i;
mAudioTrack.mSource = track;
// 创建一个另外的数据包(数据源处理)对象,其主要是用于提供已解复用音视频数据给音视频解码器
// 和缓存读取到的数据源track音视频流数据并完成解复用后的已解复用数据。
// mAudioTrack.mSource->getFormat()实现同上分析可知,
// 调用具体提取器实现类的Track实现类的该方法,获取媒体中第i个位置的Track对象信息。
// AnotherPacketSource类声明和构造函数实现,见3.4.2小节分析
mAudioTrack.mPackets =
new AnotherPacketSource(mAudioTrack.mSource->getFormat());
// 检查文件格式是否是"audio/vorbis"类型,即免费音乐格式音乐数据
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
mAudioIsVorbis = true;
} else {
mAudioIsVorbis = false;
}
// 添加当前待track的文件格式mime信息到该列表对象中
mMimes.add(String8(mime));
}
} else if (!strncasecmp(mime, "video/", 6)) {
// 视频文件格式时
// 同上分析,视频文件也只会处理第一个track视频流信息
if (mVideoTrack.mSource == NULL) {
// 同上分析
mVideoTrack.mIndex = i;
mVideoTrack.mSource = track;
mVideoTrack.mPackets =
new AnotherPacketSource(mVideoTrack.mSource->getFormat());
// 将video文件格式mime信息放在列表开始item项中
// video always at the beginning
mMimes.insertAt(String8(mime), 0);
}
}
// 添加当前track信息到track列表中
mSources.push(track);
// 获取当前track的元数据中媒体时长
// 备注:注意上面也获取了一次该值,上面那个元数据是整个文件元数据记录的文件时长,
// 而此值为当前track中数据流媒体时长,理论上只会小于而不会大于整个文件时长。
int64_t durationUs;
if (meta->findInt64(kKeyDuration, &durationUs)) {
// 只有当获取的值大于当前缓存的值才会更新它
if (durationUs > mDurationUs) {
mDurationUs = durationUs;
}
}
// 获取当前track的码流值
int32_t bitrate;
if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) {
// 然后与此前已计算好的其他track数据流总码流值相加,得到所有track数据流的总码流值
totalBitrate += bitrate;
} else {
// 注意此处理逻辑:若获取某一个track数据流的码流值失败,则直接重置总码流值为-1
totalBitrate = -1;
}
}
ALOGV("initFromDataSource mSources.size(): %zu mIsSecure: %d mime[0]: %s", mSources.size(),
mIsSecure, (mMimes.isEmpty() ? "NONE" : mMimes[0].string()));
// 判断数据流track总个数
if (mSources.size() == 0) {
ALOGE("b/23705695");
return UNKNOWN_ERROR;
}
// 检查DRM信息即版权版本管理信息,目前不用分析
// Modular DRM: The return value doesn't affect source initialization.
(void)checkDrmInfo();
// 所有可用track数据流的总码流,其值包括可能不被处理的track,例如多个音频流时只会处理第一个。
mBitrate = totalBitrate;
return OK;
}
3.4.1、MediaExtractorFactory::Create(dataSource, NULL)实现分析:
创建工厂来创建媒体提取器,不需要加锁,可能会耗时。
// [frameworks/av/media/libstagefright/MediaExtractorFactory.cpp]
// static
sp<IMediaExtractor> MediaExtractorFactory::Create(
const sp<DataSource> &source, const char *mime) {
ALOGV("MediaExtractorFactory::Create %s", mime);
// mime 传入值为空
if (!property_get_bool("media.stagefright.extractremote", true)) {
// local extractor
ALOGW("creating media extractor in calling process");
// 创建本地(线程)媒体提取器Extractor,下面分析的创建远程提取器流程会分析到该实现。
return CreateFromService(source, mime);
} else {
// 创建远程(跨进程)媒体提取器Extractor
// remote extractor
ALOGV("get service manager");
// 根据上一章节的分析可知,获取IMediaExtractorService的Bp代理端BpMediaExtractorService类对象
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
if (binder != 0) {
// 使用interface_cast转换成对应服务取得Bp代理对象,
// 即获取到了BpMediaExtractorService实现,此处转换成它的父类类型赋值给mediaExService对象
sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
// Binder机制跨进程调用该方法获取IMediaExtractor对象
// 备注:由Binder实现机制可知,该返回对象肯定是Bp代理端BpBinder类型对象即BpMediaExtractor,
// 其是在[frameworks/av/media/libmedia/IMediaExtractor.cpp]定义实现的。
// 见下面的分析
sp<IMediaExtractor> ex = mediaExService->makeExtractor(
CreateIDataSourceFromDataSource(source), mime);
// 返回BpMediaExtractor代理对象(失败则返回null)
return ex;
} else {
ALOGE("extractor service not running");
return NULL;
}
}
return NULL;
}
mediaExService->makeExtractor(CreateIDataSourceFromDataSource(source), mime)实现分析:
其中CreateIDataSourceFromDataSource(source)实现即主要就是数据对象封装转换为Binder对象,它的实现分析已在上一章节的3.1.2小节中分析,主要作用就是:通过封装DataSource对象创建IDataSource。
对于makeExtractor() 实现的分析,此处不再讲述Binder机制处理过程(请查看另外章节),直接进入Bn实现端BnMediaExtractorService子类MediaExtractorService实现者该方法分析,如下
// [frameworks/av/services/mediaextractor/MediaExtractorService.cpp]
sp<IMediaExtractor> MediaExtractorService::makeExtractor(
const sp<IDataSource> &remoteSource, const char *mime) {
ALOGV("@@@ MediaExtractorService::makeExtractor for %s", mime);
// 此处刚好和传入参数时执行相反操作,即通过封装IDataSource(BpBinder)对象来创建DataSource对象
// 见上面的3.2小节分析
sp<DataSource> localSource = CreateDataSourceFromIDataSource(remoteSource);
// 创建工厂从服务中创建媒体提取器IMediaExtractor对象
// 见下面的分析
sp<IMediaExtractor> extractor = MediaExtractorFactory::CreateFromService(localSource, mime);
ALOGV("extractor service created %p (%s)",
extractor.get(),
extractor == nullptr ? "" : extractor->name());
if (extractor != nullptr) {
// 创建成功,则注册它
// 见下面的分析
registerMediaExtractor(extractor, localSource, mime);
return extractor;
}
return nullptr;
}
MediaExtractorFactory::CreateFromService(localSource, mime)实现分析:
创建工厂从服务中创建媒体提取器IMediaExtractor对象
// [frameworks/av/media/libstagefright/MediaExtractorFactory.cpp]
sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
const sp<DataSource> &source, const char *mime) {
ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
// 涉及数字版权管理功能处理【即解密器】,不分析
// initialize source decryption if needed
source->DrmInitialization(nullptr /* mime */);
void *meta = nullptr;
void *creator = NULL;
// 释放媒体元数据的方法指针
FreeMetaFunc freeMeta = nullptr;
// 媒体提取器的得分值,分值最高的将会被指定为应使用的解复用模块,该分值范围:0 ~ 1
float confidence;
sp<ExtractorPlugin> plugin;
uint32_t creatorVersion = 0;
// 返回一个媒体解复用提取器最高得分者的创建方法指针
// 见3.4.1.1小节分析
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) {
// 强制转换为CreatorFunc方法指针类型,然后执行创建提取器方法指针,传入对应参数,
// 创建成功后返回一个指向CMediaExtractor对象的指针。
// CMediaExtractor和CreatorFunc该方法指针类型请查看上面推荐的章节内容。
// 由前面相关章节的分析可知,source对象其实是一个FileSource对象,因此调用其wrap()方法,见3.4.1.2小节分析。
CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta);
if (meta != nullptr && freeMeta != nullptr) {
// 创建成功后就请求释放相关内存
freeMeta(meta);
}
// 若创建成功,则再次将CMediaExtractor对象封装进代理对象,然后返回
// MediaExtractorCUnwrapper类声明实现,见3.4.1.3小节分析。
ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr;
}
ALOGV("Created an extractor '%s' with confidence %.2f",
ex != nullptr ? ex->name() : "<null>", confidence);
// 由于MediaExtractorCUnwrapper对象未实现Binder机制功能,
// 因此再次封装成该对象可跨进程交互的IMediaExtractor相关的子类Binder类型对象。
// 见3.4.1.4小节分析
return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin);
}
registerMediaExtractor(extractor, localSource, mime)实现分析:
其实就是将当前创建的媒体提取器对象相关信息存入媒体提取器实例ExtractorInstance对象中,然后缓存到提取器列表全局变量sExtractors中,所有进程请求创建的MediaExtractor都将记录在此处。
// [frameworks/av/media/libmedia/IMediaExtractor.cpp]
void registerMediaExtractor(
const sp<IMediaExtractor> &extractor,
const sp<DataSource> &source,
const char *mime) {
// 媒体提取器实例ExtractorInstance对象,缓存提取器插件信息
ExtractorInstance ex;
ex.mime = mime == NULL ? "NULL" : mime;
ex.name = extractor->name();
ex.sourceDescription = source->toString();
// 调用端进程ID(如上层APP的进程号)
ex.owner = IPCThreadState::self()->getCallingPid();
ex.extractor = extractor;
ex.when = time(NULL);
// 加锁代码块处理
{
Mutex::Autolock lock(sExtractorsLock);
// 若当前记录的提取器大小大于10,则重置大小为10的容量和大小,并且分配了10个空元素
if (sExtractors.size() > 10) {
sExtractors.resize(10);
}
// 添加在列表中开头位置
sExtractors.push_front(ex);
}
}
3.4.1.1、sniff() 方法实现:
// [frameworks/av/media/libstagefright/MediaExtractorFactory.cpp]
// static
void *MediaExtractorFactory::sniff(
const sp<DataSource> &source, float *confidence, void **meta,
FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion) {
*confidence = 0.0f;
*meta = nullptr;
// 加锁代码块来获取此前 MediaExtractorService 服务进程初始化时加载的解复用模块插件列表全局变量
// 关于解复用模块插件的模块化实现和注册请查看上面推荐的另一篇文章
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;
// 获取当前提取器插件配置信息,根据其版本号来执行不同版本的实现调用其插件注册时实现的sniff方法指针,
// 该指针将会返回一个用于创建该提取器插件的MediaExtractor具体实现方法指针,用来创建它。
// 关于这部分请查看上面推荐的解复用模块加载原理章节内容
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;
}
3.4.1.2、source->wrap()实现分析:
由前面相关章节的分析可知,source对象其实是一个FileSource对象,而wrap()方法使其父类DataSource实现的方法,其实现的功能也正如它的名字一样,是一个封装代理实现,如下
// [frameworks/av/media/libstagefright/include/media/stagefright/DataSource.h]
CDataSource *wrap() {
if (mWrapper) {
return mWrapper;
}
mWrapper = new CDataSource();
mWrapper->handle = this;
// 下面是赋值方法指针,注意下面的实现均是用匿名方法定义实现的,其执行最终还是DataSource自身对应的实现
// 按要求读取指定大小的文件数据
mWrapper->readAt = [](void *handle, off64_t offset, void *data, size_t size) -> ssize_t {
return ((DataSource*)handle)->readAt(offset, data, size);
};
// 获取文件数据大小
mWrapper->getSize = [](void *handle, off64_t *size) -> status_t {
return ((DataSource*)handle)->getSize(size);
};
// 当前数据源类对象的类型
mWrapper->flags = [](void *handle) -> uint32_t {
return ((DataSource*)handle)->flags();
};
// 获取当前数据源的Uri即URL路径等
mWrapper->getUri = [](void *handle, char *uriString, size_t bufferSize) -> bool {
return ((DataSource*)handle)->getUri(uriString, bufferSize);
};
// 返回封装代理实现类对象
return mWrapper;
}
3.4.1.3、MediaExtractorCUnwrapper封装代理类声明及构造函数定义:
将CMediaExtractor对象封装进代理对象。
由于篇幅长度过长,因此放入下一章节分析,请查看:
【五】Android MediaPlayer整体架构源码分析 -【prepareAsync/prepare数据准备处理流程】【Part 3】