这一节我们来看看在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");
}
}
先来一张用UML工具Astah画的prepare的序列图, 让我们对流程有个大致的认识。
我也不知道为啥每次传上来的流程图看着都不清晰,看不清楚的同学可以下载到本地再放大一点看。
序列图中的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): 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)
序列图中的mediaplayerservice之后的部分都是属于service端,它们是属于另一个进程。
./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
./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)
./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): }
./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的工作就算完成了。
在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的流程:
其实最后回过头来看,prepare做的事情也不是很多。 在我看来, 最主要的工作是创建了Extractor, 根据
这个extractor能知道媒体文件的各种信息, 比如Track数量,每个track的元数据,时长,比特率等等。