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的元数据,时长,比特率等等。
- Android N Audio播放三:prepare大揭秘
- Android N Audio播放二:setDataSource窥探
- Android N Audio播放四:start真面目
- Android N Audio播放一:如何播放一首音乐
- Android N的Audio系统(三)
- Android N Audio播放五:如何选择Extractor
- Android Audio 的播放
- Android N Audio: Audio Track play
- Android--Audio播放:竞争Audio之Audio Focus 音频焦点
- [收集]Android中的Audio播放
- MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析
- MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析
- Android Scroller大揭秘
- Android Scroller大揭秘
- Android N Audio: AudioTrack 介绍
- 三种GC大揭秘
- 三种GC大揭秘
- 三种GC大揭秘
- Zynq-Linux移植学习笔记之七-网络驱动
- 自定义View三个方法的意义
- C#编程基础->XML系列导航
- python:使用udp协议发送飞秋消息
- linux简单命令
- Android N Audio播放三:prepare大揭秘
- PAT甲级 1008.Elevator(20) 题目翻译与答案
- 安全快速更改MySQL数据库名称
- win7平台下QT软件的打包与发布(部署与安装) 打包成安装包样式
- 如何制作弹弓 - T爸写给小T的书
- LeetCode 504. Base 7
- 《The Shawshank Redemption》里的部分经典台词
- Eclipse统计代码行数
- 网络层的主要功能介绍