8. Android MultiMedia框架完全解析 - prepareAsync的过程分析
还是从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 instead
Mutex::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 start
mAtEOS = 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 creation
if (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 source
status_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 source
sp<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 source
sp<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;
else
seekPos = currentAudioTs;
IParser->enableTrack(parserHandle,trackInfo->mTrackNum, TRUE);
if(trackInfo->type == MEDIA_TEXT || trackInfo->type == MEDIA_AUDIO){
if(isTrackModeParser())
seek = true;
else
seek = 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 state
mState = 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中。
至此,这个函数的执行流程才算分析完毕。