Android N Audio播放三:prepare大揭秘

来源:互联网 发布:休闲食品行业数据 编辑:程序博客网 时间:2024/05/21 15:46

  这一节我们来看看在Android N Audio播放一:如何播放一首音乐中介绍的播放音乐的第二步:
  

player.prepare();

  同样,我们在MusicDemo中将start方法注释掉。这样可以看得更清楚。
  

    private void play(){        try {            String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/Music/Test.mp3";            player.setDataSource(path);            Log.d("Jaychou","MusicDemo setDataSource");            player.prepare();            Log.d("Jaychou","MusicDemo Prepare");            //player.start();        } catch (IOException e) {            e.printStackTrace();            Log.d("Jaychou","err when play");        }    }

1. prepare序列图

  先来一张用UML工具Astah画的prepare的序列图, 让我们对流程有个大致的认识。
  这里写图片描述

  我也不知道为啥每次传上来的流程图看着都不清晰,看不清楚的同学可以下载到本地再放大一点看。

2. prepare都干了些啥

2.1 Client端

  序列图中的MusicDemo, MediaPlayer.java, Mediaplayer.cpp都是属于Client端,他们属于同一个进程。
  

这里写图片描述

一直调到Mediaplayer.cpp的prepare.

./frameworks/av/media/libmedia/Mediaplayer.cpp#prepare

status_t MediaPlayer::prepare(){    ALOGV("prepare");    Mutex::Autolock _l(mLock);    mLockThreadId = getThreadId();    if (mPrepareSync) {        mLockThreadId = 0;        return -EALREADY;    }    mPrepareSync = true;    status_t ret = prepareAsync_l();    if (ret != NO_ERROR) {        mLockThreadId = 0;        return ret;    }    if (mPrepareSync) {        mSignal.wait(mLock);  // wait for prepare done        mPrepareSync = false;    }    ALOGV("prepare complete - status=%d", mPrepareStatus);    mLockThreadId = 0;    return mPrepareStatus;

再到

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, mPlayer(%p)", mCurrentState, mPlayer.get());    return INVALID_OPERATION;}

可以看到,client端主要是调用service端的方法来设置Parameter, AudioStreamType和调用具体播放器(上一节我们知道是Nuplayer)的prepare方法. 还有一个是任务就是设置VideoSurfaceTexture,这个是在JNI层调用下来的,在日志中也可以看到:

02-13 18:57:15.769 V/MediaPlayer( 4477): setVideoSurfaceTexture02-13 18:57:15.772 V/MediaPlayerService(  616): [4] setVideoSurfaceTexture(0x0)02-13 18:57:15.774 V/MediaPlayerService(  616): [4] setAudioStreamType(3)

2.2 Service端

序列图中的mediaplayerservice之后的部分都是属于service端,它们是属于另一个进程。

这里写图片描述

2.2.1 MediaPlayerService

./frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp#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();#if CALLBACK_ANTAGONIZER    ALOGD("start Antagonizer");    if (ret == NO_ERROR) mAntagonizer->start();#endif    return ret;}

这里主要是调用NuplayerDriver的prepareAsync的方法。

02-13 18:57:15.775 V/MediaPlayerService(  616): [4] prepareAsync

2.2.2 NuplayerDriver

./frameworks/av/media/libmediaplayerservice/nuplayer/NuplayerDriver.cpp#prepareAsync

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, 这里走的是case STATE_UNPREPARED, 这是因为在上一步的setDataSource设置完成后, 回调用NuplayerDriver的notifySetDataSourceCompleted方法,在这个方法中把mState置为了STATE_UNPREPARED。

void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {    Mutex::Autolock autoLock(mLock);    CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);    mAsyncResult = err;    mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;    mCondition.broadcast();}
02-13 18:57:15.775 V/NuPlayerDriver(  616): prepareAsync(0xe802b140)

2.2.3 Nuplayer

./frameworks/av/media/libmediaplayerservice/nuplayer/Nuplayer.cpp#prepareAsync

Nuplayer的prepareAsync发送kWhatPrepare消息出去。

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

在onMessageReceived中接收处理。
./frameworks/av/media/libmediaplayerservice/nuplayer/Nuplayer.cpp#onMessageReceived

void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {    ALOGE("onMessageReceived msg is %s", msg->debugString().c_str());    switch (msg->what()) {        ......        case kWhatPrepare:        {            mSource->prepareAsync();            break;        }        ......    }}

这里的Log信息是我加的,这样加可以清楚的看到接收到的是什么消息。这里又去调用GenericSource的prepareAsync方法。

02-13 18:57:15.776 E/NuPlayer(  616): onMessageReceived msg is AMessage(what = 'prep', target = 7) = {02-13 18:57:15.776 E/NuPlayer(  616): }

2.2.4 GenericSource

./frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp#prepareAsync

void NuPlayer::GenericSource::prepareAsync() {    if (mLooper == NULL) {        mLooper = new ALooper;        mLooper->setName("generic");        mLooper->start();        mLooper->registerHandler(this);    }    sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this);    msg->post();}

通过AMessage发送kWhatPrepareAsync消息。 同样,在onMessageReceived中接收处理。

./frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp#onMessageReceived

void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {    switch (msg->what()) {      case kWhatPrepareAsync:      {          onPrepareAsync();          break;      }      ......   }}

这里我就没有加Log信息了,和上面Nuplayer是相同的原理。所以真正做的事情是在onPrepareAsync中。

./frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp#onPrepareAsync

这个函数代码较多,这里我们只关心关键的部分。

void NuPlayer::GenericSource::onPrepareAsync() {    // delayed data source creation    if (mDataSource == NULL) {        ALOGE("onPrepareAsync  mDataSource is null");        // set to false first, if the extractor        // comes back as secure, set it to true then.        mIsSecure = false;        ......        //1.构造datasource        mDataSource = new FileSource(mFd, mOffset, mLength);          mFd = -1;        ......        //2.根据datasource来生产对应的Extractor        // init extractor from data source        status_t err = initFromDataSource();        ......        //3.通知Flag的变化        ALOGE("GenericSource notifyFlagsChanged");        notifyFlagsChanged(        (mIsSecure ? FLAG_SECURE : 0)        | (mDecryptHandle != NULL ? FLAG_PROTECTED : 0)        | FLAG_CAN_PAUSE        | FLAG_CAN_SEEK_BACKWARD        | FLAG_CAN_SEEK_FORWARD        | FLAG_CAN_SEEK);        ......        //4. 完成prepare        ALOGE("GenericSource finishPrepareAsync");        finishPrepareAsync();             ......    }}

2.2.4.1 构造datasource
虽然setDataSource已经构建source, 但有些source可能没有及时生成,没有生成的source就在prepare中构建。

2.2.4.2 根据datasource来生产对应的Extractor
./frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp#initFromDataSource
这个函数的代码同样较多。

status_t NuPlayer::GenericSource::initFromDataSource() {    ALOGE("initFromDataSource");    sp<IMediaExtractor> extractor;    ......    //1.根据datasource创建extractor,我们这里是得到MP3Extractor     extractor = MediaExtractor::Create(mDataSource,         mimeType.isEmpty() ? NULL : mimeType.string());    ......    int32_t totalBitrate = 0;    //根据extractor得到track的数量    size_t numtracks = extractor->countTracks();    if (numtracks == 0) {        return UNKNOWN_ERROR;    }    for (size_t i = 0; i < numtracks; ++i) {        sp<IMediaSource> track = extractor->getTrack(i);        if (track == NULL) {            continue;        }        //获取每个track的元数据        sp<MetaData> meta = extractor->getTrackMetaData(i);        if (meta == NULL) {            ALOGE("no metadata for track %zu", i);            return UNKNOWN_ERROR;        }         ......        //得到时长        int64_t durationUs;        if (meta->findInt64(kKeyDuration, &durationUs)) {            if (durationUs > mDurationUs) {                mDurationUs = durationUs;            }        }        //得到比特率        int32_t bitrate;        if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) {            totalBitrate += bitrate;        } else {            totalBitrate = -1;        }    

可以看到,prepare最主要的工作就是在这里了。

02-13 18:57:15.777 V/MediaExtractor(  616): MediaExtractor::Create (null)02-13 18:57:15.792 V/MediaExtractor(  616): get service manager02-13 18:57:15.796 V/MediaExtractorService(  614): @@@ MediaExtractorService::makeExtractor for (null)02-13 18:57:15.797 V/MediaExtractor(  614): MediaExtractor::CreateFromService (null)02-13 18:57:15.797 V/MediaExtractor(  616): readAt(0, 2048)02-13 18:57:15.800 V/MediaExtractor(  616): readAt(0, 8202)02-13 18:57:15.824 V/MediaExtractor(  614): Autodetected media content as 'audio/mpeg' with confidence 0.8002-13 18:57:15.824 I/MediaExtractor(  614): extractor created in uid: 1040 (mediaex)02-13 18:57:15.825 V/MediaExtractor(  616): readAt(0, 2048)02-13 18:57:15.837 V/MediaExtractorService(  614): extractor service created 0xf30976c0 (MP3Extractor)02-13 18:57:15.837 V/MediaExtractorService(  614): extractor service  ret->name is MP3Extractor

有些源码我就没有贴出来了, 有兴趣的同学可以去查看android N的源代码。

2.2.4.3 通知Flag的变化
调用到nuplayer的notifyFlagsChanged方法。设置source的Flag.
./frameworks/av/media/libmediaplayerservice/nuplayer/Nuplayer.cpp#notifyFlagsChanged

void NuPlayer::Source::notifyFlagsChanged(uint32_t flags) {    sp<AMessage> notify = dupNotify();    notify->setInt32("what", kWhatFlagsChanged);    notify->setInt32("flags", flags);    notify->post();}

./frameworks/av/media/libmediaplayerservice/nuplayer/Nuplayer.cpp#onSourceNotify

case Source::kWhatFlagsChanged:{    uint32_t flags;    CHECK(msg->findInt32("flags", (int32_t *)&flags));    sp<NuPlayerDriver> driver = mDriver.promote();    if (driver != NULL) {        if ((flags & NuPlayer::Source::FLAG_CAN_SEEK) == 0) {            driver->notifyListener(                    MEDIA_INFO, MEDIA_INFO_NOT_SEEKABLE, 0);        }        driver->notifyFlagsChanged(flags);    }    if ((mSourceFlags & Source::FLAG_DYNAMIC_DURATION)            && (!(flags & Source::FLAG_DYNAMIC_DURATION))) {        cancelPollDuration();    } else if (!(mSourceFlags & Source::FLAG_DYNAMIC_DURATION)            && (flags & Source::FLAG_DYNAMIC_DURATION)            && (mAudioDecoder != NULL || mVideoDecoder != NULL)) {        schedulePollDuration();    }    mSourceFlags = flags;    break;}

这里的flag是15,转成二进制就是1111. 支持暂停,向前seek, 向后seek, seek. 每个二进制代表的含义:

 enum Flags {     FLAG_CAN_PAUSE          = 1,     FLAG_CAN_SEEK_BACKWARD  = 2,  // the "10 sec back button"     FLAG_CAN_SEEK_FORWARD   = 4,  // the "10 sec forward button"     FLAG_CAN_SEEK           = 8,  // the "seek bar"     FLAG_DYNAMIC_DURATION   = 16,     FLAG_SECURE             = 32,     FLAG_PROTECTED          = 64, };
02-13 18:57:15.848 E/NuPlayer(  616): what is AMessage(what = 'srcN', target = 7) = {02-13 18:57:15.848 E/NuPlayer(  616):   int32_t what = 102-13 18:57:15.848 E/NuPlayer(  616):   int32_t flags = 1502-13 18:57:15.848 E/NuPlayer(  616): } 

2.2.4.4 完成prepare
GenericSource调用到Nuplayer
./frameworks/av/media/libmediaplayerservice/nuplayer/Nuplayer.cpp#onSourceNotify

case Source::kWhatPrepared:{    ALOGE("Source::kWhatPrepared is ");    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) {        // 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();    } else {        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;        if (mSource->getDuration(&durationUs) == OK) {            driver->notifyDuration(durationUs);        }        driver->notifyPrepareCompleted(err);    }    break;}

这里检查err是否ok.

02-13 18:57:15.851 E/NuPlayer(  616): what is AMessage(what = 'srcN', target = 7) = {02-13 18:57:15.851 E/NuPlayer(  616):   int32_t what = 002-13 18:57:15.851 E/NuPlayer(  616):   int32_t err = 002-13 18:57:15.851 E/NuPlayer(  616): } 

err为0, 即prepare的工作就算完成了。

2.3 service端到Client端的回调

在Nuplayer的prepare设置成功以后, 会使用notify一层一层往回通知设置成功的消息。
这里我就以日志来表示了。

02-13 18:57:15.852 D/NuPlayerDriver(  616): notifyListener_l(0xe802b140), (1, 0, 0), loop setting(0, 0)02-13 18:57:15.852 V/MediaPlayerService(  616): [4] notify (0xe6803240, 1, 0, 0)02-13 18:57:15.853 V/MediaPlayer( 4477): message received msg=1, ext1=0, ext2=002-13 18:57:15.853 V/MediaPlayer( 4477): prepared02-13 18:57:15.853 V/MediaPlayer( 4477): signal application thread02-13 18:57:15.853 V/MediaPlayer( 4477): callback application02-13 18:57:15.853 V/MediaPlayer( 4477): prepare complete - status=002-13 18:57:15.853 E/MediaPlayer-JNI( 4477): JNIMediaPlayerListener notify02-13 18:57:15.866 D/Jaychou ( 4477): MusicDemo Prepare

这里的数字1就代表prepare

每个数字代表的event时间如下:

./frameworks/av/include/media/Mediaplayer.h

enum media_event_type {    MEDIA_NOP               = 0, // interface test message    MEDIA_PREPARED          = 1,    MEDIA_PLAYBACK_COMPLETE = 2,    MEDIA_BUFFERING_UPDATE  = 3,    MEDIA_SEEK_COMPLETE     = 4,    MEDIA_SET_VIDEO_SIZE    = 5,    MEDIA_STARTED           = 6,    MEDIA_PAUSED            = 7,    MEDIA_STOPPED           = 8,    MEDIA_SKIPPED           = 9,    MEDIA_TIMED_TEXT        = 99,    MEDIA_ERROR             = 100,    MEDIA_INFO              = 200,    MEDIA_SUBTITLE_DATA     = 201,    MEDIA_META_DATA         = 202,};

用一张总的流程图来总结一下prepare的流程:

这里写图片描述

3. 总结

其实最后回过头来看,prepare做的事情也不是很多。 在我看来, 最主要的工作是创建了Extractor, 根据
这个extractor能知道媒体文件的各种信息, 比如Track数量,每个track的元数据,时长,比特率等等。

0 0
原创粉丝点击