前面的章节中我们了解到上层调用setDataSource后,MediaPlayerService::Client(IMediaPlayer)会调用MediaPlayerFactory创建MediaPlayerBase。Android为我们提供了默认的播放器实现NuPlayer,NuPlayerDriver实现了MediaPlayerBase接口,内部调用并管理有NuPlayer,起着承上启下的作用。
本节代码参考:
frameworks/av/media/libmediaplayerservice/include/MediaPlayerInterface.h
frameworks/av/media/libmediaplayerservice/nuplayer
MediaPlayerBase
是一个抽象类,定义了播放器需要的基本接口,并给出了一些方法的默认实现,如果我们想要实现播放器并且接入MediaPlayer,那么必须要继承于 MediaPlayerBase。
MediaPlayerBase::Listener
是一个内部抽象类,通过它可以将事件上抛给上层 MediaPlayerService::Client,MediaPlayerService::Client 中有 Listener 的具体实现。
MediaPlayerBase::AudioSink
同样是内部抽象类,定义了 Audio Output 所需要的基本接口,这个类用于走AudioTrack / AudioFlinger 进行软件合成输出声音,MediaPlayerService::AudioOutput
是 software output 的具体实现。
MediaPlayerInterface
继承于 MediaPlayerBase,实现了 hardwareOutput 方法,它走的是 software output,需要用到上面提到的 AudioOutput。我们接下来要看的 NuPlayerDriver 继承自于这个类,所以声音走的软件合成。
MediaPlayerHWInterface
也是继承于 MediaPlayerBase,从名字上就可以看出它走的是 hardware output,是没有setAudioSink接口的,具体如何进行硬件合成需要 vendor 自己来实现。
通常来说,我们实现的播放器需要继承于 MediaPlayerInterface / MediaPlayerHWInterface,确定声音走软件还是硬件合成。如果我们的播放器需要两者都支持,也可以直接继承于MediaPlayerBase,或者继承于MediaPlayerInterface。
MediaPlayerBase 提供有基础而全面的播放接口,但是如果我们实现的播放器还有更多的功能,可以使用它提供的 invoke
函数来实现自定义接口的调用。
NuPlayerDriver 会对上层下发的指令进行处理,根据当前的状态调用 NuPlayer 对应的功能。
NuPlayer 内部使用的是 AMessage - AHandler - ALooper 这一套异步消息处理机制,从观察来看播放控制接口,例如 setDataSource、prepare、start、pause、reset 这类播控制接口都是异步调用的(NuPlayer 没有 stop);设定或获取一些参数的接口,例如 getTrackInfo、getSelectedTrack 这类都是同步调用的。
回到 NuPlayerDriver 中来,通过搜索 mCondition.wait 来看哪些接口是同步调用的,查找到 setDataSource、setVideoSurfaceTexture、prepare 和 reset 这四个接口是同步调用的。为什么他们四个是同步调用的,而其他的 start、pause、stop 是异步调用的呢?
我的理解是这样:异步调用的接口会依赖同步调用的4个接口所创建的对象,例如 setDataSource 会创建出 NuPlayer 中的 Source 对象,如果这步是异步的,Source 还没有创建出来就调用 prepare,那就会出现空指针的错误了;又比如 reset 会销毁 MediaPlayerClient 及其内部的对象,如果是异步的,reset处理过程中重新调用其他接口,也是很有可能出现空指针的问题。所以这类会创建或者销毁成员对象的接口必须要进行同步调用!
而 start、pause、stop 这类接口并不会创建或者销毁某些成员变量,不会对前后调用产生影响,调用后会在 ALooper 中按照调用顺序执行,所以异步处理不会有问题。
我们再看下 NuPlayer 中的基本播放控制方法,总共有 setDataSourceAsync
、prepareAsync
、start
、pause
和 resetAsync
这5个,是没有stop 的,NuPlayer 的 stop 就是用的 pause 来假装的。
接下来再看 NuPlayer 有哪些状态,状态如下:
enum State {
STATE_IDLE,
STATE_SET_DATASOURCE_PENDING,
STATE_UNPREPARED,
STATE_PREPARING,
STATE_PREPARED,
STATE_RUNNING,
STATE_PAUSED,
STATE_RESET_IN_PROGRESS,
STATE_STOPPED, // equivalent to PAUSED
STATE_STOPPED_AND_PREPARING, // equivalent to PAUSED, but seeking
STATE_STOPPED_AND_PREPARED, // equivalent to PAUSED, but seek complete
};
STATE_IDLE
:MediaPlayerService::Client 调用 setDataSource 刚刚创建 NuPlayerDriver 时状态为 STATE_IDLE,reset 之后状态也变成 STATE_IDLE;STATE_SET_DATASOURCE_PENDING
:调用 MediaPlayerBase setDataSource 后状态变成 STATE_SET_DATASOURCE_PENDING,这个过程可能会比较耗时,所以在等待过程中设置了这个中间状态,如果有人错误使用了多线程调用MediaPlayer,那么这个中间状态将会阻止这个错误调用;STATE_UNPREPARED
:setDataSource 之后的状态置为 STATE_UNPREPARED;STATE_PREPARING
:同样的,prepare过程可能会比较耗时,所以也设置了这个中间状态;prepare 有同步和异步两个版本,同步版本的作用和 SET_DATASOURCE_PENDING 作用相同;异步的版本会标记当前的状态为 preparing,当 prepareAsync 过程中调用 reset 销毁对象时,会直接退出 STATE_PREPARING 状态,进入到 reset 的状态中;STATE_RUNNING
:正在播放的状态,这个状态下 isPlaying 接口返回值为 true;STATE_PAUSED
:暂停状态为STATE_PAUSED,播放结束的状态也是STATE_PAUSED;STATE_STOPPED
:停止播放状态;STATE_RESET_IN_PROGRESS
:reset 的处理过程会将状态置为 STATE_RESET_IN_PROGRESS;STATE_STOPPED_AND_PREPARING
:stop 之后需要重新 prepare 才能继续播放,stop 时资源都没有释放,所以是直接 seek 到文件起始位置播放;但是这里的 preparing 并不会影响 reset 的动作。STATE_STOPPED_AND_PREPARED
:stop 之后 prepare 调用完成,状态置为 STATE_STOPPED_AND_PREPARED。status_t NuPlayerDriver::start_l() {
switch (mState) {
// 1、
case STATE_UNPREPARED:
{
status_t err = prepare_l();
CHECK_EQ(mState, STATE_PREPARED);
FALLTHROUGH_INTENDED;
}
// 2
case STATE_PAUSED:
case STATE_STOPPED_AND_PREPARED:
case STATE_PREPARED:
{
mPlayer->start();
FALLTHROUGH_INTENDED;
}
// 3
case STATE_RUNNING:
{
if (mAtEOS) {
mPlayer->seekToAsync(0);
mAtEOS = false;
mPositionUs = -1;
}
break;
}
default:
return INVALID_OPERATION;
}
mState = STATE_RUNNING;
return OK;
}
从以上代码可以看出,start 的处理情况有4种:
status_t NuPlayerDriver::pause() {
ALOGD("pause(%p)", this);
int unused;
getCurrentPosition(&unused);
Mutex::Autolock autoLock(mLock);
switch (mState) {
// 1
case STATE_PAUSED:
case STATE_PREPARED:
return OK;
// 2
case STATE_RUNNING:
mState = STATE_PAUSED;
notifyListener_l(MEDIA_PAUSED);
mPlayer->pause();
break;
default:
return INVALID_OPERATION;
}
return OK;
}
pause 的处理情况有3种:
status_t NuPlayerDriver::stop() {
ALOGD("stop(%p)", this);
Mutex::Autolock autoLock(mLock);
switch (mState) {
// 1
case STATE_RUNNING:
mPlayer->pause();
FALLTHROUGH_INTENDED;
// 2
case STATE_PAUSED:
mState = STATE_STOPPED;
notifyListener_l(MEDIA_STOPPED);
break;
// 3
case STATE_PREPARED:
case STATE_STOPPED:
case STATE_STOPPED_AND_PREPARING:
case STATE_STOPPED_AND_PREPARED:
mState = STATE_STOPPED;
break;
// 4
default:
return INVALID_OPERATION;
}
return OK;
}
status_t NuPlayerDriver::reset() {
ALOGD("reset(%p) at state %d", this, mState);
updateMetrics("reset");
logMetrics("reset");
Mutex::Autolock autoLock(mLock);
switch (mState) {
// 1
case STATE_IDLE:
return OK;
// 2
case STATE_SET_DATASOURCE_PENDING:
case STATE_RESET_IN_PROGRESS:
return INVALID_OPERATION;
// 3
case STATE_PREPARING:
{
CHECK(mIsAsyncPrepare);
notifyListener_l(MEDIA_PREPARED);
break;
}
default:
break;
}
if (mState != STATE_STOPPED) {
notifyListener_l(MEDIA_STOPPED);
}
mState = STATE_RESET_IN_PROGRESS;
mPlayer->resetAsync();
while (mState == STATE_RESET_IN_PROGRESS) {
mCondition.wait(mLock);
}
return OK;
}
reset 的处理情况有3种:
MEDIA_PREPARED
,如果当前状态不是 stop则还要回调上层当前的状态为 STATE_STOPPED,所以我们上层应用在 prepareAsync、stop 的回调事件中应该判断当前 reset 是否被调用,如果被调用了则不应该 call start 方法,防止快切时出现问题;最后调用resetAsync,等待 reset 完成。我们要注意的是 MediaPlayerService::Client 和 NuPlayerDriver 的 reset 方法并不会销毁对象,只有等MediaPlayer Native 的 disconnect 调用完成 MediaPlayerService 中的对象才会销毁。
如果在 prepareAsync 的状态下调用 reset,prepare 事件处理完成后会调用回调事件 notifyPrepareCompleted,这里面会怎么处理呢?
void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
ALOGV("notifyPrepareCompleted %d", err);
Mutex::Autolock autoLock(mLock);
if (mState != STATE_PREPARING) {
// 直接退出
CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
return;
}
CHECK_EQ(mState, STATE_PREPARING);
mAsyncResult = err;
if (err == OK) {
mState = STATE_PREPARED;
if (mIsAsyncPrepare) {
notifyListener_l(MEDIA_PREPARED);
}
} else {
mState = STATE_UNPREPARED;
if (mIsAsyncPrepare) {
notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
}
}
sp<MetaData> meta = mPlayer->getFileMeta();
int32_t loop;
if (meta != NULL
&& meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
mAutoLoop = true;
}
mCondition.broadcast();
}
从代码里可以看到,如果 prepareAsync 完成时,则直接退出回调方法,不会向上 Callback。
视频播放结束后,NuPlayer 会调用 Callback 上抛 MEDIA_PLAYBACK_COMPLETE 事件,如果设置了 Loop 则自动 seek 到开头位置开始播放,否则调用 pause 方法进入暂停的状态,并且将 mAtEOS
设置为 true,最终将 MEDIA_PLAYBACK_COMPLETE 回调给上层。
void NuPlayerDriver::notifyListener_l(
int msg, int ext1, int ext2, const Parcel *in) {
switch (msg) {
case MEDIA_PLAYBACK_COMPLETE:
{
if (mState != STATE_RESET_IN_PROGRESS) {
if (mAutoLoop) {
audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
if (mAudioSink != NULL) {
streamType = mAudioSink->getAudioStreamType();
}
if (streamType == AUDIO_STREAM_NOTIFICATION) {
mAutoLoop = false;
}
}
// 1、如果有循环播放则seek到开始位置,然后直接返回
if (mLooping || mAutoLoop) {
mPlayer->seekToAsync(0);
if (mAudioSink != NULL) {
mAudioSink->start();
}
return;
}
// 2、否则进入暂停状态
mPlayer->pause();
mState = STATE_PAUSED;
}
FALLTHROUGH_INTENDED;
}
case MEDIA_ERROR:
{
if (msg == MEDIA_ERROR) {
Mutex::Autolock autoLock(mMetricsLock);
if (mMetricsItem != NULL) {
mMetricsItem->setInt32(kPlayerError, ext1);
if (ext2 != 0) {
mMetricsItem->setInt32(kPlayerErrorCode, ext2);
}
mMetricsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
}
}
// 3、将 mAtEOS 设置为true,标记当前状态
mAtEOS = true;
break;
}
default:
break;
}
mLock.unlock();
// 4、callback到上层
sendEvent(msg, ext1, ext2, in);
mLock.lock();
}