还是从mediaplayer.cpp文件开始分析:

status_t MediaPlayer::prepareAsync()
{ALOGV("prepareAsync");Mutex::Autolock _l(mLock);return prepareAsync_l();
}

基本没做什么,设置了一个自动锁,然后就直接跳到MediaPlayer::prepareAsync_l中去执行了:

status_t MediaPlayer::prepareAsync_l()
{if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {if (mAudioAttributesParcel != NULL) {mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);} else {mPlayer->setAudioStreamType(mStreamType);}mCurrentState = MEDIA_PLAYER_PREPARING;return mPlayer->prepareAsync();}ALOGE("prepareAsync called in state %d", mCurrentState);return INVALID_OPERATION;
}

这里有两个函数要执行,第一个函数:setAudioStreamType,这里的mPlayer是IMediaPlayer这个匿名Binder Server,会通过IMediaPlayer最终调用到MediaPlayerService::Client::setAudioStreamType函数:

status_t MediaPlayerService::Client::setAudioStreamType(audio_stream_type_t type)
{ALOGV("[%d] setAudioStreamType(%d)", mConnId, type);// TODO: for hardware output, call player insteadMutex::Autolock l(mLock);if (mAudioOutput != 0) mAudioOutput->setAudioStreamType(type);return NO_ERROR;
}

之后就是prepareAsync函数,同样是通过IMediaPlayer最终调用到MediaPlayerService::Client::prepareAsync函数:

status_t MediaPlayerService::Client::prepareAsync()
{ALOGV("[%d] prepareAsync", mConnId);sp<MediaPlayerBase> p = getPlayer();if (p == 0) return UNKNOWN_ERROR;status_t ret = p->prepareAsync();return ret;
}

在之前的分析中说过,这里的p得到的是NuPlayerDriver,然后就是调用

status_t NuPlayerDriver::prepareAsync() {ALOGV("prepareAsync(%p)", this);Mutex::Autolock autoLock(mLock);switch (mState) {case STATE_UNPREPARED:mState = STATE_PREPARING;mIsAsyncPrepare = true;mPlayer->prepareAsync();return OK;case STATE_STOPPED:// this is really just paused. handle as seek to startmAtEOS = false;mState = STATE_STOPPED_AND_PREPARING;mIsAsyncPrepare = true;mPlayer->seekToAsync(0, true /* needNotify */);return OK;default:return INVALID_OPERATION;};
}

NuPlayerDriver根据mState的状态来选择执行哪一个分支,刚执行到这里,当前的状态一般是STATE_UNPREPARED,所以会执行 mPlayer->prepareAsync(),而在NuPlayerDriver中的mPlayer是NuPlayer,所以就会调用到NuPlayer的prepareAsync函数,而这个函数的实现更为简单:

void NuPlayer::prepareAsync() {(new AMessage(kWhatPrepare, this))->post();
}

继续查找NuPlayer::onMessageReceived函数中的实现:

case kWhatPrepare:{mSource->prepareAsync();break;}

发现就直接调用到mSource里面的函数了,这个mSource在setDataSource函数中设置了,为GenericSource,那么继续向下看:

这个NuPlayer::GenericSource继承自NuPlayer::Source,而NuPlayer::Source又继承自AHandler,所以在GenericSource中也可以使用AHandler-Amessage-ALooper机制,在这个函数中创建了ALooper,并且设置Looper的Handler为这个GenericSource,然后发送kWhatPrepareAsync这个AMessage来交给onMessageReceived函数来运行,最终运行到NuPlayer::GenericSource::onPrepareAsync函数中(GenericSource.cpp):

void NuPlayer::GenericSource::onPrepareAsync() {// delayed data source creationif (mDataSource == NULL) {// set to false first, if the extractor// comes back as secure, set it to true then.mIsSecure = false;if (!mUri.empty()) {const char* uri = mUri.c_str();String8 contentType;mIsWidevine = !strncasecmp(uri, "widevine://", 11);if (!strncasecmp("http://", uri, 7)|| !strncasecmp("https://", uri, 8)|| mIsWidevine) {mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);if (mHttpSource == NULL) {ALOGE("Failed to create http source!");notifyPreparedAndCleanup(UNKNOWN_ERROR);return;}}mDataSource = DataSource::CreateFromURI(mHTTPService, uri, &mUriHeaders, &contentType,static_cast<HTTPBase *>(mHttpSource.get()));} else {mIsWidevine = false;mDataSource = new FileSource(mFd, mOffset, mLength);mFd = -1;}if (mDataSource == NULL) {ALOGE("Failed to create data source!");notifyPreparedAndCleanup(UNKNOWN_ERROR);return;}}if (mDataSource->flags() & DataSource::kIsCachingDataSource) {mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());}// For widevine or other cached streaming cases, we need to wait for// enough buffering before reporting prepared.// Note that even when URL doesn't start with widevine://, mIsWidevine// could still be set to true later, if the streaming or file source// is sniffed to be widevine. We don't want to buffer for file source// in that case, so must check the flag now.mIsStreaming = (mIsWidevine || mCachedSource != NULL);// init extractor from data sourcestatus_t err = initFromDataSource();先截取到这里,后面的代码分析到了再放出来。
}

这个函数挺长的,一点一点分析。

在函数的开始,还没有对mDataSource赋值,同时!mUri.empty() = 0, 所以会走到

mDataSource = new FileSource(mFd, mOffset, mLength);这个分支。

这里又出来一个DataSource,同时

class FileSource : public DataSource

struct NuCachedSource2 : public DataSource

class DataSource是抽象出来的一个Source父类,不同的Source都是这个类的子类。

下一个重要的函数就是initFromDataSource了,这个函数的注释是:init extractor from data source,函数是:NuPlayer::GenericSource::initFromDataSource()

在这个函数中,首先对mIsWidevine,mIsStreaming进行判断,在上层的NuPlayer::GenericSource::onPrepareAsync函数中,对于普通的FileSource,都设置这两者为Null,这两者是与Widevine和实时视频流相关的,所以走到MediaExtractor的创建阶段:

extractor = MediaExtractor::Create(mDataSource,

mimeType.isEmpty() ? NULL : mimeType.string());

会根据DataSource的类型来创建对应的Extractor。具体MediaExtractor::Create函数的执行过程在《10. MediaExtractor::Create函数的解析和FslExtractor分析》中详细介绍了,它通过sniff函数检测出媒体类型,然后创建出对应Extractor,而对于FSL的平台,创建出来的就是FslExtractor,创建好以后,就可以开始解析文件内容了。

mFileMeta = extractor->getMetaData();

继续看《10. MediaExtractor::Create函数的解析和FslExtractor分析--- 三》

继续回到NuPlayer::GenericSource::initFromDataSource()函数中,执行完extractor->getMetaData()后,我们也就知道了文件中所包含的track数和里面的数据,打印出下面的话:

GenericSource: mFileMeta = 1.

说明mFileMeta数据不为空,这时候就通过mFileMeta->findInt64(kKeyDuration, &duration)来获取文件的Duration数据。

然后通过extractor->countTracks()来统计文件中Track的数目,可以看到FslExtractor::countTracks()函数的实现:

size_t FslExtractor::countTracks()
{status_t ret = OK;if(!bInit){ret = Init();if(ret != OK)return 0;}return mTracks.size();
}

发现这个函数里面,只是判断Init函数有没有执行,因为countTracks这些操作都是在Init函数中做的。

然后通过一个for循环来遍历这些tracks,将从Extractor中解析出来的metadata等等数据,保存在GenericSource结构体中的MVideoTrack和mAudioTrack中。

终于分析完initFromDataSource函数了,下面继续跳回NuPlayer::GenericSource::onPrepareAsync()函数中。。。。

void NuPlayer::GenericSource::onPrepareAsync() {
。。。。。。。。。
继续上面的代码,从initFromDataSource()后面开始off64_t size;if(mCachedSource != NULL && mCachedSource->getSize(&size) == OK && mDurationUs > 0){ALOGV("file size is %lld, duration is %lld", size, mDurationUs);int64_t bitrate = size * 8000000ll / mDurationUs;// When bitrate is larger than 15Mbps, use calculated watermarks.if(bitrate > 15 * 1024 * 1024){size_t lowWaterMark = bitrate / 8 * (kLowWaterMarkUs / 1000000 + 3) ;size_t highWaterMark = bitrate / 8 * (kHighWaterMarkUs / 1000000 + 3);ALOGI("bitrate is %lld, set new cache watermark to %d - %d", bitrate, lowWaterMark, highWaterMark);char s[30];sprintf(s,"%zd/%zd/%d", lowWaterMark/1000, highWaterMark/1000, -1);mCachedSource->updateCacheParamsFromString(s);}}if (err != OK) {ALOGE("Failed to init from data source!");notifyPreparedAndCleanup(err);return;}
//上面这段代码应该不会执行。if (mVideoTrack.mSource != NULL) {sp<MetaData> meta = doGetFormatMeta(false /* audio */);sp<AMessage> msg = new AMessage;err = convertMetaDataToMessage(meta, &msg);if(err != OK) {notifyPreparedAndCleanup(err);return;}notifyVideoSizeChanged(msg);}uint32_t flags =(mIsSecure ? FLAG_SECURE : 0)| (mDecryptHandle != NULL ? FLAG_PROTECTED : 0)| FLAG_CAN_PAUSE ;uint32_t extractor_flags = mExtractor->flags();if(extractor_flags & MediaExtractor::CAN_SEEK)flags |= FLAG_CAN_SEEK;if(extractor_flags & MediaExtractor::CAN_SEEK_FORWARD)flags |= FLAG_CAN_SEEK_FORWARD;if(extractor_flags & MediaExtractor::CAN_SEEK_BACKWARD)flags |= FLAG_CAN_SEEK_BACKWARD;ALOGV("flags %x", flags);notifyFlagsChanged(flags);if (mIsSecure) {// secure decoders must be instantiated before starting widevine sourcesp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, this);notifyInstantiateSecureDecoders(reply);} else {finishPrepareAsync();}
}

在doGetFormatMeta函数内部,通过source->getFormat()来获取到Track中的format,然后调用到notifyVideoSizeChanged函数,这个函数是NuPlayer::Source里面的函数:

NuPlayer::Source::notifyVideoSizeChanged,这时候已经跳转到NuPlayer.cpp中执行了,最后执行到updateVideoSize里面,打印出:

NuPlayer: Video input format 1024 x 768

同时在这个函数的最后,执行:

notifyListener(MEDIA_SET_VIDEO_SIZE,displayWidth,displayHeight);

把设置的Video的width和height通知出来,通过下面的函数跳到NuPlayerDriver中:

void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) {if (mDriver == NULL) {return;}sp<NuPlayerDriver> driver = mDriver.promote();if (driver == NULL) {return;}driver->notifyListener(msg, ext1, ext2, in);
}void NuPlayerDriver::notifyListener(int msg, int ext1, int ext2, const Parcel *in) {Mutex::Autolock autoLock(mLock);notifyListener_l(msg, ext1, ext2, in);
}

在NuPlayerDriver::notifyListener_l函数中,只是对MEDIA_PLAYBACK_COMPLETE和MEDIA_ERROR进行了处理,并没有对MEDIA_SET_VIDEO_SIZE进行处理,所以执行默认的default操作,然后通过 sendEvent(msg, ext1, ext2, in);NuPlayerDriver的上一层时MediaPlayerService,所以这时候就到达了MediaPlayerService::Client::notify函数中,打印出:

MediaPlayerService: [1] notify (0xb3ee73c0, 5, 1024, 768)

这句话。

打印后,执行c->notify(msg, ext1, ext2, obj);,这时候位于MediaPlayerService.cpp中,所以执行这个notify是IMediaPlayerClient,到达IMediaPlayerClient的Bp端,再传递到Bn端。

class MediaPlayer : public BnMediaPlayerClient,

public virtual IMediaDeathNotifier

所以Bn端的代码会传递到MediaPlayer类中,然后就在MediaPlayer::notify函数中(mediaplayer.cpp)进行处理。这个notify的传递过程与现在探讨的函数传递方向相反。正常是mediaplayer.cpp--->MediaPlayerService--->NuPlayerDriver--->NuPlayer

这个方向是相反的:NuPlayer--->NuPlayerDriver--->MeidaPlayerService--->mediaplayer.cpp

然后MediaPlayer::notify函数打印出接收到的msg:

MediaPlayer: message received msg=5, ext1=1024, ext2=768

然后处理这个msg,打印出下面的话:

ALOGV("New video size %d x %d", ext1, ext2);

继续在NuPlayer::GenericSource::onPrepareAsync()函数中运行:

从mExtractor中获取文件的flag参数,这些参数包括:FLAG_CAN_PAUSE,FLAG_CAN_SEEK_BACKWARD,FLAG_CAN_SEEK_FORWARD,FLAG_CAN_SEEK,FLAG_DYNAMIC_DURATION等等。最终打印出来的是这些位按位与的结果,比如这个文件可以执行的操作flag就是:f。

最后,还有一个很重要的知识点,就是最后有个finishPrepareAsync()函数,看看上面的NuPlayer::GenericSource::finishPrepareAsync()函数中的表述:

if (mIsSecure) {// secure decoders must be instantiated before starting widevine sourcesp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, this);notifyInstantiateSecureDecoders(reply);} else {finishPrepareAsync();}

如果是secure decoders的话,就需要在这里通过notifyInstantiateSecureDecoders函数来初始化Decoder,但是目前播放的是普通的MP4文件,所以会调用下面的finishPrepareAsync函数,这个函数一不小心就漏掉了:

void NuPlayer::GenericSource::finishPrepareAsync() {status_t err = startSources();if (err != OK) {ALOGE("Failed to init start data source!");notifyPreparedAndCleanup(err);return;}if (mIsStreaming) {mPrepareBuffering = true;ensureCacheIsFetching();restartPollBuffering();} else {notifyPrepared();}
}

就是我标记红色的代码,也很容易遗漏,先看第一段代码(startSources()):

status_t NuPlayer::GenericSource::startSources() {if (mAudioTrack.mSource != NULL && mAudioTrack.mSource->start() != OK) {ALOGE("failed to start audio track!");return UNKNOWN_ERROR;}if (mVideoTrack.mSource != NULL && mVideoTrack.mSource->start() != OK) {ALOGE("failed to start video track!");return UNKNOWN_ERROR;}return OK;
}

这个函数让Track的source开始运行,由于这个文件中只有VideoTrack,所以执行 mVideoTrack.mSource->start(),跳到FslMediaSource::start函数中(FslExtractor.cpp):

status_t FslMediaSource::start(MetaData * /* params */)
{mStarted = true;mExtractor->ActiveTrack(mSourceIndex);ALOGD("source start track %d",mSourceIndex);return OK;
}

继续追踪(FslExtractor.cpp):

status_t FslExtractor::ActiveTrack(uint32 index)
{uint64 seekPos = 0;Mutex::Autolock autoLock(mLock);bool seek = true;TrackInfo *trackInfo = &mTracks.editItemAt(index);if(trackInfo == NULL)return UNKNOWN_ERROR;trackInfo->bCodecInfoSent = false;if(trackInfo->type == MEDIA_VIDEO){seekPos = currentVideoTs;mVideoActived = true;}else if(trackInfo->type == MEDIA_AUDIO)seekPos = currentAudioTs;else if(currentVideoTs > 0)seekPos = currentVideoTs;elseseekPos = currentAudioTs;IParser->enableTrack(parserHandle,trackInfo->mTrackNum, TRUE);if(trackInfo->type == MEDIA_TEXT || trackInfo->type == MEDIA_AUDIO){if(isTrackModeParser())seek = true;elseseek = false;}if(seek)IParser->seek(parserHandle, trackInfo->mTrackNum, &seekPos, SEEK_FLAG_NO_LATER);ALOGD("start track %d",trackInfo->mTrackNum);return OK;
}

核心是IParser->enableTrack(parserHandle,trackInfo->mTrackNum, TRUE);函数,还是lib库中提供的接口。

然后看标红的第二段代码(notifyPrepared()):

跳转到NuPlayer::Source::notifyPrepared中去执行了:

void NuPlayer::Source::notifyPrepared(status_t err) {sp<AMessage> notify = dupNotify();notify->setInt32("what", kWhatPrepared);notify->setInt32("err", err);notify->post();
}

发送消息,到NuPlayer::onSourceNotify中去执行,最终是找到NuPlayerDriver:

driver->notifyPrepareCompleted(err);

继续跳转:

void NuPlayerDriver::notifyPrepareCompleted(status_t err) {Mutex::Autolock autoLock(mLock);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);mAsyncResult = err;if (err == OK) {// update state before notifying client, so that if client calls back into NuPlayerDriver// in response, NuPlayerDriver has the right statemState = STATE_PREPARED;if (mIsAsyncPrepare) {notifyListener_l(MEDIA_PREPARED);}} else {mState = STATE_UNPREPARED;if (mIsAsyncPrepare) {notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);}}sp<MetaData> meta = mPlayer->getFileMeta();int32_t loop;if (meta != NULL&& meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {mAutoLoop = true;}mCondition.broadcast();
}

这里notifyListener_l(MEDIA_PREPARED)直接调用到NuPlayerDriver::notifyListener_l()函数,

然后同样通过IMediaPlayerClient的Bp端传到Bn端,再传递到mediaplayer.cpp中。

至此,这个函数的执行流程才算分析完毕。

8. Android MultiMedia框架完全解析 - prepareAsync的过程分析相关推荐

  1. Android MultiMedia框架完全解析 - 概览

    之前的工作中,一直在看Android MultiMedia的一些东西,关注我博客的同学也许知道我换工作了,以后将要从事Camera相关的工作,于是乎,将之前整理存放在有道云笔记里面的一些东西发出来,整 ...

  2. 10. Android MultiMedia框架完全解析 - MediaExtractor::Create函数的解析和FslExtractor分析

    先来看看MediaExtractor所处的位置: (一)创建流程 在GenericSource.cpp的NuPlayer::GenericSource::initFromDataSource()函数中 ...

  3. 腾讯零反射全动态Android插件框架Shadow解析

    简介 最近几年,腾讯对于开源事业也是越来越支持,今天要说的就是在腾讯被广泛使用的Shadow框架,一个经过线上亿级用户量检验的反射全动态Android插件框架. 首先,让我们来看一下官方对于Shado ...

  4. 太全了,一线互联网大厂都在用的Android UI框架完全解析,拿去吧你

    在学习Android过程中,会使用到很多UI框架,而使用时对框架的实现方式应有一定的了解,这个过程最好的方式就是阅读源码,学习大厂的使用方法.但UI框架很多,不时会有新的出现,而且对一些通用框架来说, ...

  5. Android Multimedia框架总结(二十八)NuPlayer到OMX过程

    原址 NuPlayer是谷歌新研发的.  AwesomePlayer存在BUG,谷歌早已在android m 版本中弃用. sp<MediaPlayerBase> MediaPlayerS ...

  6. Android Multimedia框架总结(二十四)MediaMuxer实现手机屏幕录制成gif图

    原址:http://blog.csdn.net/hejjunlin/article/details/53866405 前言:上篇中,介绍是用MediaMuxer与MediaExtractor进入音视频 ...

  7. Android Multimedia框架总结(十七)音频开发基础知识

    原文链接:http://blog.csdn.net/hejjunlin/article/details/53078828 近年来,唱吧,全民K歌,QQ音乐,等成为音频软件的主流力量,音频开发一直是多媒 ...

  8. Android UI框架深度解析

    UI界面,对于每个应用而言,是它与用户进行交互的门脸.好的门脸,不只是是要亮丽可人,最好还能秀色可餐过目不忘,甚至还应该有涵养有气质,彬彬有理温柔耐心. 对于开发者来说,锻造这样的面容,不但需要高超的 ...

  9. Android开源框架——网络解析GSON

    准备工作 GSON下载地址:http://download.csdn.net/detail/wiseclown/9496184 官网地址:https://github.com/google/gson ...

最新文章

  1. 深度探秘 从 Auto Labeler 挖掘 Tesla 全自动驾驶的工作机制
  2. APNIC执委赵巍:IPv4向IPv6过渡再无退路
  3. 组合表头_单双斜线表头——520,想单就单,想双就双
  4. python的模块文档_python查看模块文档
  5. 反转一个值中的最后n位
  6. Python数模笔记-StatsModels 统计回归(1)简介
  7. 关于iconfont的一些东西
  8. Excel·VBA数组排列函数
  9. time库:Python的时间时钟处理
  10. 逍遥书生服务器啥时候维护完毕,《逍遥西游2》12月8日服务器维护公告
  11. Arduino ESP32 TFTLCD ST7735 代码和原理
  12. LVGL8的窗口切换方式
  13. 百度地图线路颜色_旅游厕所电子地图:让“方便”更方便
  14. JavaScript葵花宝典(基础)
  15. 小程序轮播图_厉害!对准不认识的野生菌拍个图,云大学生设计的这个小程序就能自动识别…...
  16. 2022图像翻译/扩散模型:UNIT-DDPM: UNpaired Image Translation with Denoising Diffusion Probabilistic Models
  17. spoon无法初始化至少一个步骤_通俗易懂:8大步骤图解注意力机制
  18. 中华英才网爬虫程序(3)-queue和threading模块的结合使用
  19. 什么是码率控制? 在视频编码中,码率控制的概念是什么,它是通过什么实现的?
  20. 【tflearn系列教程】(二)如何安装tflearn

热门文章

  1. cesium 之三维场景展示篇(附源码下载)
  2. Final Cut Pro X Guru: Motion Graphics in Motion and After Effects Final Cut Pro X Guru:运动和After Effe
  3. 复杂业务系统的架构设计思路
  4. 子豪兄-YOLOv3
  5. Mac:TexStudio 中文论文模版
  6. 单片机中数制与数制的转换是怎样实现的
  7. 使用UltraISO刻录DMG光盘映像
  8. syscall常量解释(持续完善中)
  9. 老牌好用免费的数据恢复软件easyrecovery操作简单一键恢复
  10. 36 | 职业发展:应聘安全工程师,我需要注意什么?