Android N Audio播放四:start真面目
来源:互联网 发布:9860计算器怎么编程 编辑:程序博客网 时间:2024/06/07 06:06
start是MusicDemo中播放音乐的最后一步,也是最重要的一步。这里我们就得把三步都打开了,后面为了看清楚,可以把前面两步的日志先过滤出去。
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(); Log.d("Jaychou","MusicDemo start"); } catch (IOException e) { e.printStackTrace(); Log.d("Jaychou","err when play"); } }
1. start流程图
和之前一样,还是先来看一看start的流程图,其实和setDataSource和prepare几乎是一样的。
2. client端
如之前一样,MusicDemo, MediaPlayer.java和MediaPlayer.cpp都是属于Client端,它们是跑在同一个进程里面。
这里也简单用下图示例一下。
从MusicDemo一直到MediaPlayer.cpp的start
./frameworks/av/media/libmedia/Mediaplayer.cpp#start
status_t MediaPlayer::start(){ ALOGV("start"); status_t ret = NO_ERROR; Mutex::Autolock _l(mLock); mLockThreadId = getThreadId(); if (mCurrentState & MEDIA_PLAYER_STARTED) { ret = NO_ERROR; } else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) { mPlayer->setLooping(mLoop); mPlayer->setVolume(mLeftVolume, mRightVolume); mPlayer->setAuxEffectSendLevel(mSendLevel); mCurrentState = MEDIA_PLAYER_STARTED; ret = mPlayer->start(); if (ret != NO_ERROR) { mCurrentState = MEDIA_PLAYER_STATE_ERROR; } else { if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) { ALOGV("playback completed immediately following start()"); } } } else { ALOGE("start called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get()); ret = INVALID_OPERATION; } mLockThreadId = 0; return ret;}
可以看到,这里也没有做多少事情,就是调用server端来设置是否循环,音量等参数,当然,最重要的是调用server端的start。
以下是一些相关的日志可供参考。
02-17 17:51:59.379 V/MediaPlayer-JNI( 5974): start02-17 17:51:59.379 V/MediaPlayer( 5974): start02-17 17:51:59.380 V/MediaPlayerService( 642): [4] setLooping(0)02-17 17:51:59.380 V/MediaPlayerService( 642): [4] setVolume(1.000000, 1.000000)02-17 17:51:59.381 V/AudioSink( 642): setVolume(1.000000, 1.000000)02-17 17:51:59.382 V/MediaPlayerService( 642): [4] setAuxEffectSendLevel(0.000000)02-17 17:51:59.383 V/AudioSink( 642): setAuxEffectSendLevel(0.000000)
3. server端
MediaPlayerService之后的类是跑在server端,它们在另一个进程中。
3.1 MediaPlayerService
./frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp#start
status_t MediaPlayerService::Client::start(){ ALOGV("[%d] start", mConnId); sp<MediaPlayerBase> p = getPlayer(); if (p == 0) return UNKNOWN_ERROR; p->setLooping(mLoop); return p->start();}
这里非常简单,就是调用下一步NuPlayerDriver的start方法。
02-17 17:51:59.384 V/MediaPlayerService( 642): [4] start
3.2 NuPlayerDriver
./frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp#start
status_t NuPlayerDriver::start() { ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS); Mutex::Autolock autoLock(mLock); return start_l();}
./frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp#start_l
status_t NuPlayerDriver::start_l() { switch (mState) { case STATE_UNPREPARED: { status_t err = prepare_l(); if (err != OK) { return err; } CHECK_EQ(mState, STATE_PREPARED); // fall through } case STATE_PAUSED: case STATE_STOPPED_AND_PREPARED: case STATE_PREPARED: { mPlayer->start(); // fall through } case STATE_RUNNING: { if (mAtEOS) { mPlayer->seekToAsync(0); mAtEOS = false; mPositionUs = -1; } break; } default: return INVALID_OPERATION; } mState = STATE_RUNNING; return OK;}
这里mState是STATE_PREPARED,这是在prepare的时候把状态置成STATE_PREPARED的。看来也没干啥,就是调用Nuplayer的start方法。
02-17 17:51:59.385 D/NuPlayerDriver( 642): start(0xee80c600), state is 4, eos is 0
3.3 NuPlayer
./frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp#start
void NuPlayer::start() { (new AMessage(kWhatStart, this))->post();}
通过AMessage post出去。最终调用onStart
02-17 17:51:59.385 E/NuPlayer( 642): Nuplayer onMessageReceived msg is AMessage(what = 'strt', target = 7) = {02-17 17:51:59.385 E/NuPlayer( 642): }02-17 17:51:59.385 V/NuPlayer( 642): kWhatStart
./frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp#onStart
这段代码较多,只列出关键。
void NuPlayer::onStart(int64_t startPositionUs) { //1.调用GenericSource的start if (!mSourceStarted) { mSourceStarted = true; mSource->start(); } ...... //2.创建renderer mRenderer = AVNuFactory::get()->createRenderer(mAudioSink, notify, flags); mRendererLooper = new ALooper; mRendererLooper->setName("NuPlayerRenderer"); mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO); mRendererLooper->registerHandler(mRenderer); status_t err = mRenderer->setPlaybackSettings(mPlaybackSettings); if (err != OK) { mSource->stop(); mSourceStarted = false; notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); return; } ...... //3.调用postScanSources, 这里面会创建解码器。 ALOGE("onStart postScanSources"); postScanSources();}
3.3.1 调用GenericSource的start
./frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp#startvoid NuPlayer::GenericSource::start() { ALOGI("start"); mStopRead = false; if (mAudioTrack.mSource != NULL) { ALOGI("start mAudioTrack.mSource not null"); postReadBuffer(MEDIA_TRACK_TYPE_AUDIO); } if (mVideoTrack.mSource != NULL) { ALOGI("start mVideoTrack.mSource not null"); postReadBuffer(MEDIA_TRACK_TYPE_VIDEO); } setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000); mStarted = true; (new AMessage(kWhatStart, this))->post();}
这里有一步比较重要的是postReadBuffer(MEDIA_TRACK_TYPE_AUDIO),这里会去读取audio 的buffer, 具体的操作我们在后面会有单独的文章来介绍,这里暂时不详说。
接着发送kWhatStart消息,最后到
void NuPlayer::GenericSource::BufferingMonitor::restartPollBuffering() { Mutex::Autolock _l(mLock); if (mIsStreaming) { cancelPollBuffering_l(); onPollBuffering_l(); }}
这个主要是流媒体相关的,这里就先不介绍了。
02-17 17:51:59.385 I/GenericSource( 642): start02-17 17:51:59.386 I/GenericSource( 642): start mAudioTrack.mSource not null02-17 17:51:59.386 E/GenericSource( 642): GenericSource onMessageReceived msg is AMessage(what = 0x0000000c, target = 8) = {02-17 17:51:59.386 E/GenericSource( 642): int32_t trackType = 202-17 17:51:59.386 E/GenericSource( 642): }
what = 0x0000000c代表的是kWhatReadBuffer消息。
3.3.2 创建renderer
这里的渲染, 我们放在以后的文章中来介绍。
3.3.3 调用postScanSources, 这里面会创建解码器
./frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp#postScanSources
void NuPlayer::postScanSources() { if (mScanSourcesPending) { return; } sp<AMessage> msg = new AMessage(kWhatScanSources, this); msg->setInt32("generation", mScanSourcesGeneration); msg->post(); mScanSourcesPending = true;}
./frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp#onMessageReceived
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGE("Nuplayer onMessageReceived msg is %s", msg->debugString().c_str()); switch (msg->what()) { ...... case kWhatScanSources: { int32_t generation; CHECK(msg->findInt32("generation", &generation)); if (generation != mScanSourcesGeneration) { // Drop obsolete msg. break; } mScanSourcesPending = false; ALOGV("scanning sources haveAudio=%d, haveVideo=%d", mAudioDecoder != NULL, mVideoDecoder != NULL); bool mHadAnySourcesBefore = (mAudioDecoder != NULL) || (mVideoDecoder != NULL); bool rescan = false; // initialize video before audio because successful initialization of // video may change deep buffer mode of audio. if (mSurface != NULL) { if (instantiateDecoder(false, &mVideoDecoder) == -EWOULDBLOCK) { rescan = true; } } // Don't try to re-open audio sink if there's an existing decoder. if (mAudioSink != NULL && mAudioDecoder == NULL) { if (instantiateDecoder(true, &mAudioDecoder) == -EWOULDBLOCK) { rescan = true; } } if (!mHadAnySourcesBefore && (mAudioDecoder != NULL || mVideoDecoder != NULL)) { // This is the first time we've found anything playable. if (mSourceFlags & Source::FLAG_DYNAMIC_DURATION) { schedulePollDuration(); } } status_t err; if ((err = mSource->feedMoreTSData()) != OK) { if (mAudioDecoder == NULL && mVideoDecoder == NULL) { // We're not currently decoding anything (no audio or // video tracks found) and we just ran out of input data. if (err == ERROR_END_OF_STREAM) { notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0); } else { notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); } } break; } if (rescan) { msg->post(100000ll); mScanSourcesPending = true; } break; } .... }}
这里会通过instantiateDecoder创建音视频的解码器,我们将在之后的文章讨论这个问题。
4. server端到Client端的notify
同prepare一样,当start开始以后,会一层层往上通知当前的状态。
02-17 17:51:59.761 D/NuPlayerDriver( 642): notifyListener_l(0xee80c600), (6, 0, 0), loop setting(0, 0)02-17 17:51:59.761 V/MediaPlayerService( 642): [4] notify (0xf004f540, 6, 0, 0)02-17 17:51:59.763 E/MediaPlayer-JNI( 5974): JNIMediaPlayerListener notify
5. 总结
可以看到,start的流程和prepare几乎都是一样的。
但start开始以后,是怎么读取audio数据,是如何解码的等等问题,我们在接下来的文章中会慢慢介绍。
- Android N Audio播放四:start真面目
- Android N Audio播放二:setDataSource窥探
- Android N的Audio系统(四)
- Android N Audio播放一:如何播放一首音乐
- Android N Audio播放三:prepare大揭秘
- Android N Audio播放五:如何选择Extractor
- Android Audio 的播放
- Android多媒体学习四:调用Android自带的播放器播放Audio
- Android N Audio: Audio Track play
- Android--Audio播放:竞争Audio之Audio Focus 音频焦点
- [收集]Android中的Audio播放
- MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析
- MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析
- Android N Audio: AudioTrack 介绍
- Android Media Server - MediaPlayer - start (audio)
- Android中的Audio播放:控制Audio输出通道切换
- Android中的Audio播放:控制Audio输出通道切换
- Android中的Audio播放:控制Audio输出通道切换
- 5-10 公路村村通 (30分)
- 逆向供应链服务平台
- Java设计模式学习(3)- Chain Of Responsibility模式 (下)
- 项目
- 本地人的平等观念和糟粕化
- Android N Audio播放四:start真面目
- Java 线程面试题 Top 50
- 《时间简史》读感
- android下tcp之client测试
- Android项目之用户管理
- git学习笔记(一)
- iOS-TCP/IP、Http、Socket的区别
- 5个常用的Python功能代码
- 长链剖分随想