setDataSource是使用MediaPlayer播放音乐文件的重要步骤。这个操作会去创建NuplayerDriver,Nuplayer,然后根据传递的参数(文件信息)创建GenericSource。为后续的prepare或者prepareAsync操作作准备。
1.NuPlayerFactory:工具类。在本章中,它用于构造NuplayerDriver。
2.MediaPlayerFactory:工具类。在本章中,它用于创建NuPlayerFactory和TestPlayerFactory。
3.NuplayerDriver:它是Nuplayer的wrapper。它继承了MediaPlayerInterface接口,然后MediaPlayerInterface继承了MediaPlayerBase。MediaPlayerService对Nuplayer的访问是通过NuplayerDriver的父类MediaPlayerInterface来实现的。所以它相当于一个中转站。
4.Nuplayer::GenericSource:解析模块。从extractor中读取解封装之后的文件数据。它是Nuplayer::Source的子类。
5.Nuplayer::Decoder:解码模块。在播放的时候会调用MediaCodec去获取编解码列表,选择合适解码库。
6.MediaCodec:Decoder的代工。根据Decoder的要求,去MediaCodecList中查找解码器信息,然后选择合适的解码库。负责跟ACodec进行数据交互。
7.MediaCodecList:用于获取编解码信息列表,这些信息是从OmxStore那里获取的。OmxStore是一个HAL server(也就是hidl的server端)。在media.codec进程中,会注册OmxStore和Omx服务,OmxStore先把解码器信息解析出来,然后保存在自己的成员变量中。
8.Nuplayer:这个类主要是扮演操控者的角色,根据NuplayerDriver的指令去控制Decoder和Source,也就是协调解析和解码的工作。
9.ACodec:主要负责解码相关工作。通过IOMXNode去控制解码器:设置参数,获取参数等。将从ACodecBufferChannel获取的解析之后的文件数据送给解析器。
10.MediaPlayerService::Client:它继承于BnMediaPlayer,用于上层对音乐播放流程的控制。
我把setDataSource,prepareAsync,start这几个重要步骤放在同一张时序图里面。目的是为了对nuplay的工作流程有一个整体上的认识。这一节只讲setDataSource流程,所以只需看setDataSource那一部分就行了。
Nuplayer、Decoder、GenericSource中有些控制块是有颜色的。因为这三个类都有消息发送处理这种异步机制,所以为了更好的表示时序图,我把消息的源和目标都标成同样的颜色。
setDataSource过程可以分解为下面几个子流程:
1.创建NuPlayerDriver和NuPlayer。返回NuPlayerDriver的父类指针MediaPlayerBase指针给MediaPlayerService::Client。
2.通过NuPlayerDriver提供的setDataSource以异步的方式去创建GenericSource,然后把文件描述符等信息保存到GenericSource中。
3.设置NuPlayerDriver状态,为下一个步骤prepareAsync(下一节会讲)做准备。
下面对这三点进行展开讲述
MediaPlayerService::Client在创建NuPlayerDriver的时候并不是直接new NuPlayerDriver,而是通过NuPlayerFactory这一个工具类来创建的。这个类只是让Client多了一个选择而已。在android 6.0中有三个选择StagefrightPlayerFactory、NuPlayerFactory、TestPlayerFactory。
但是高版本已经把StagefrightPlayerFactory去掉了,也就是说AwesomePlayer已经在高版本中消失了。创建出来的NuplayerDriver保存在全局变量sFactoryMap中,并且设置它的索引key为NU_PLAYER=4。在创建的时候会传递pid给NuPlayerDriver,这个pid是调用者的pid。比如我用安卓原生的music播放音乐,那这个pid就是com.android.music的。
MediaPlayerFactory::tFactoryMap MediaPlayerFactory::sFactoryMap;
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
return new NuPlayerDriver(pid); //这里会把调用者的pid传递进去给它们
}
}
class TestPlayerFactory : public MediaPlayerFactory::IFactory {
virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) {
return new TestPlayerStub();
}
}
创建NuPlayerDriver的时候会去创建NuPlayer,并保存为自己的成员变量mPlayer。由于NuPlayer是继承于AHandler,所以还得给它new 一个ALooper提供一个循环。这个过程没啥操作,这里就不赘述了。
NuPlayerDriver是以异步的方式设置数据源的。
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
mPlayer->setDataSourceAsync(fd, offset, length); //异步的方式去设置,调用NuPlayer::setDataSourceAsync
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock); //在这里等待Nuplayer处理完这个流程
}
}
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
sp<GenericSource> source =new GenericSource(notify, mUIDValid, mUID, mMediaClock);
status_t err = source->setDataSource(fd, offset, length);
msg->setObject("source", source);
msg->post();
}
从总时序图中,可以看出new GenericSource之后的source->setDataSource仅仅是将文件描述符等信息保存到GenericSource中。由于这里的设置源是异步调用,所以需要NuPlayer给NuPlayerDriver答复。
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
case kWhatSetDataSource:{
status_t err = OK;
sp<RefBase> obj;
CHECK(msg->findObject("source", &obj)); //取出setDataSourceAsync中设置的source,类型是sp
if (obj != NULL) {
Mutex::Autolock autoLock(mSourceLock);
mSource = static_cast<Source *>(obj.get()); //取出GenericSource,并转化成父类指针Source *
} else {
err = UNKNOWN_ERROR;
}
sp<NuPlayerDriver> driver = mDriver.promote(); //NuPlayer中的NuPlayerDriver对象是弱引用,需要升级为sp指针
if (driver != NULL) {
driver->notifySetDataSourceCompleted(err); //通知NuPlayerDriver
}
break;
}
}
void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE; //设置状态,这个状态很重要
mCondition.broadcast(); //发送信号。NuPlayerDriver::setDataSource中mCondition.wait(mLock)操作会被唤醒
}
可以看出setDataSource这个操作仅仅是设置数据源信息而已,并没有去加载解析器和解码器。在notifySetDataSourceCompleted中有一个重要的操作就是:当返回的状态OK的时候会设置mState的状态为STATE_UNPREPARED,也就是未准备的状态。所以从这里我们也可以看出下一个操作就是prepare操作了