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, 所以会走到

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();

initFromDataSource已经在上一篇文章中介绍过了。

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();//1
    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(),跳到xxxMediaSource::start函数中(xxxExtractor.cpp):
举例MPEG4Extractor.cpp中的MPEG4Source

status_t MPEG4Source::start(MetaData *params) {
    Mutex::Autolock autoLock(mLock);

    CHECK(!mStarted);

    int32_t val;
    if (params && params->findInt32(kKeyWantsNALFragments, &val)
        && val != 0) {
        mWantsNALFragments = true;
    } else {
        mWantsNALFragments = false;
    }

    int32_t tmp;
    CHECK(mFormat->findInt32(kKeyMaxInputSize, &tmp));
    size_t max_size = tmp;

    // A somewhat arbitrary limit that should be sufficient for 8k video frames
    // If you see the message below for a valid input stream: increase the limit
    const size_t kMaxBufferSize = 64 * 1024 * 1024;
    if (max_size > kMaxBufferSize) {
        ALOGE("bogus max input size: %zu > %zu", max_size, kMaxBufferSize);
        return ERROR_MALFORMED;
    }
    if (max_size == 0) {
        ALOGE("zero max input size");
        return ERROR_MALFORMED;
    }

    // Allow up to kMaxBuffers, but not if the total exceeds kMaxBufferSize.
    const size_t kMaxBuffers = 8;
    const size_t buffers = min(kMaxBufferSize / max_size, kMaxBuffers);
    mGroup = new MediaBufferGroup(buffers, max_size);
    mSrcBuffer = new (std::nothrow) uint8_t[max_size];
    if (mSrcBuffer == NULL) {
        // file probably specified a bad max size
        delete mGroup;
        mGroup = NULL;
        return ERROR_MALFORMED;
    }

    mStarted = true;

    return OK;
}

然后看标红的第二段代码(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中。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值