一、前言:
之前的博客,有分析过Awesome player,但是随着Android版本的升高,Awesome已经被淘汰,现在的消费电子产品,更青睐于使用Android原生的NuPlayer,NuPlayer是一款native层的应用级播放器,使用的是Android更为推崇的MediaCodec组件,下层和Awesome一样,也是基于omx来解码的,从这篇博客开始,将基于Android5.1
版本对NuPlayer源码进行一个分析。本博客先来梳理NuPlayer的创建流程,看下nuplayer是如何调用到MediaCodec的,NuPlayer的流程我们紧抓Source---Renderer---Decoder
三部分来分析。
二、源码分析:
1.MediaPlayer的setdatasource操作:
NuPlayer是通过MediaPlayer在native层评分选择到的,关于MediaPlayer对MediaPlayerService的binder调用我们就不讲了,不太清楚的可以去看下我之前的博客:
好了,回到MediaPlayerFactory.cpp,应用调用mediaplayer的setdatasource
指令后,会通过MediaPlayerService服务评分找到NuPlayer,就会去创建播放器:
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
player_type playerType)
{
...
ALOGV("setDataSource_pre player type = %d", playerType);
// create the right type of player
/* 1.根据已评分类型去创建播放器 */
sp<MediaPlayerBase> p = createPlayer(playerType);
if (p == NULL) {
ALOGE("setDataSource_pre error !!!!");
return p;
}
/* 注意这里会去创建AudioSink */
if (!p->hardwareOutput()) {
mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
mPid, mAudioAttributes);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
}
mPlayerType = playerType;
return p;
}
上面代码的注释中,需要注意第二点,p对应的类是MediaPlayerBase,这里面有一个纯虚函数hardwareOutput(),用来确定子类播放器是否需要创建AudioSink用作音频输出。我们看下Nuplayer的继承关系:
因为nuplayerdriver是nuplayer的对外封装,继承自MediaPlayerInterface类,所以,
MediaPlayerInterface必然实现了hardwareOutput()这个纯虚函数:
class MediaPlayerInterface : public MediaPlayerBase
{
...
virtual bool hardwareOutput() {
return false; }
...
}
也就是说,走nuplayer的时候,下层是一定会去创建AudioSink来输出音频的,为什么会有这个接口的原因是,有些厂商会用到自己定制化的native层播放器,不会走官方的AudioSink来播放音频,故该函数为纯虚函数。看一下nuplayerdriver的构造函数:
NuPlayerDriver::NuPlayerDriver()
: mState(STATE_IDLE),
mIsAsyncPrepare(false),
mAsyncResult(UNKNOWN_ERROR),
mSetSurfaceInProgress(false),
mDurationUs(-1),
mPositionUs(-1),
mSeekInProgress(false),
mLooper(new ALooper),
mPlayerFlags(0),
mAtEOS(false),
mLooping(false),
mAutoLoop(false),
mStartupSeekTimeUs(-1) {
ALOGE("NuPlayerDriver(%p)", this);
mLooper->setName("NuPlayerDriver Looper");
mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_AUDIO);
mPlayer = new NuPlayer;
mLooper->registerHandler(mPlayer);
mPlayer->setDriver(this);
}
nuplayer和mediacodec中大量使用了消息机制来调用函数,nuplayerdriver的构造函数我们需要注意初始化状态为STATE_IDLE
,另外实例化了一个ALooper和nuplayer,并将nuplayer注册到了这个ALooper中,这是因为nuplayerdriver是继承自MediaPlayerInterface类,不具有消息处理能力,而nuplayer则是继承自AHandler类,是具有消息处理能力的
。在nuplayer及mediacodec中,很多类都是继承自AHandler类的,这个需要注意。
NuPlayer的构造函数除了做一些变量的初始化,没有做其他特别的事情。
在实例化播放器之后,紧接着会去调用播放器的setdatasource接口,看一下实现:
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGE("setDataSource(%p) file(%d)", this, fd);
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
return INVALID_OPERATION;
}
mState = STATE_SET_DATASOURCE_PENDING;
/* 调用nuplayer的setdatasource */
mPlayer->setDataSourceAsync(fd, offset, length);
/* 等待nuplayer执行setdatasource完成 */
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
}
return mAsyncResult;
}
nuplayerdriver用了一个信号量来等待下层的nuplayer执行setdatasource完成,跟进到nuplayer:
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
/* 实例化通用source */
sp<GenericSource> source =
new GenericSource(notify, mUIDValid, mUID);
/*调用source的setdatasource */
status_t err = source->setDataSource(fd, offset, length);
if (err != OK) {
ALOGE("Failed to set data source!");
source = NULL;
}
/* 发送消息,用于改变状态 */
msg->setObject("source", source);
msg->post();
}
mediaplayer会根据我们的URL自动为我们选择一个合适的setdatasource函数,代码以播放本地文件为例,GenericSource的实例化和setdatasource都没有什么需要分析的,我们来看下消息机制的发送,因为前面说了nuplayer继承自AHandler的,所以直接看nuplayer里面的kWhatSetDataSource消息处理:
case kWhatSetDataSource:
{
ALOGV("kWhatSetDataSource");
CHECK(mSource == NULL);
status_t err = OK;
sp<RefBase> obj;
CHECK(msg->findObject("source", &obj));
if (obj != NULL) {
mSource = static_cast<Source *>(obj.get());
} else {
err = UNKNOWN_ERROR;
}
CHECK(mDriver != NULL);
sp<NuPlayerDriver> driver = mDriver.promote();
/* 通知nuplayerdriver */
if (driver != NULL) {
driver->notifySetDataSourceCompleted(err);
}
break;
}
消息的处理主要是通知nuplayerdriver,看一下notifySetDataSourceCompleted:
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();
}
如果nuplayerdriver的状态是STATE_SET_DATASOURCE_PENDING
的话,就修改为
STATE_UNPREPARED
。
2.MediaPlayer的prepare操作:
不论是Mediaplayer的prepare还是prepareAsync(网络流),最终都会调用到nuplayer的prepareAsync,先看下nuplayerdriver的prepare_l实现:
status_t NuPlayerDriver::prepare_l() {
switch (mState) {
case STATE_UNPREPARED:
mState = STATE_PREPARING;
// Make sure we're not posting any notifications, success or
// failure information is only communicated through our result
// code.
mIsAsyncPrepare = false;
/* 调用nuplayer的prepareAsync() */
mPlayer->prepareAsync();
/* 确保prepare函数执行完成 */
while (mState == STATE_PREPARING) {
mCondition.wait(mLock);
}
return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR;
...
}