Android N Audio播放三:prepare大揭秘

  这一节我们来看看在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的序列图, 让我们对流程有个大致的认识。
  Android N Audio播放三:prepare大揭秘_第1张图片

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

2. prepare都干了些啥

2.1 Client端

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

Android N Audio播放三:prepare大揭秘_第2张图片

一直调到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): setVideoSurfaceTexture
02-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端,它们是属于另一个进程。

Android N Audio播放三:prepare大揭秘_第3张图片

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 manager
02-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.80
02-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 = 1
02-13 18:57:15.848 E/NuPlayer(  616):   int32_t flags = 15
02-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 = 0
02-13 18:57:15.851 E/NuPlayer(  616):   int32_t err = 0
02-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=0
02-13 18:57:15.853 V/MediaPlayer( 4477): prepared
02-13 18:57:15.853 V/MediaPlayer( 4477): signal application thread
02-13 18:57:15.853 V/MediaPlayer( 4477): callback application
02-13 18:57:15.853 V/MediaPlayer( 4477): prepare complete - status=0
02-13 18:57:15.853 E/MediaPlayer-JNI( 4477): JNIMediaPlayerListener notify
02-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的流程:

Android N Audio播放三:prepare大揭秘_第4张图片

3. 总结

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

你可能感兴趣的:(Android多媒体)