NuPlayer源碼分析一:播放器創建
文章目錄
源碼環境:Oreo 8.0.0_r4
需要編譯 /frameworks/av/media/libmediaplayerservice,生成libmediaplayerservice.so
該系列播放將詳細分析NuPlayer源碼,經過該系列博客之後,你應該就可以對底層播放器有了一個具體而全面的認識,系列文章分爲下面幾個部分:
簡介
看過或者沒看過MediaPlayer源碼分析一文的朋友,都應該知道,嚴格意義上講,MediaPlayer並不是播放器本身,它只是Android框架層衆多媒體播放器(包括ROM廠商自定義的播放器)的“殼“。
MediaPlayer的所有主要操作(包括但不限於播放、暫停、快進等)都將通過調用鏈條,到達框架層的播放器,如NuPlayer。那麼,這裏就來分析一下,Android框架層真正的播放器,NuPlayer。
MediaPlayer和NuPlayer的關係如圖:
NuPlayer前情提要
既然MediaPlayer
只是NuPlayer
等底層播放器的殼,那麼是不是new MediaPlayer()
的時候,底層就會new NuPlayer()呢?
結論是否定的,對於NuPlayer播放器來說,NuPlayer類中,實現了播放相關函數,但播放器的控制流程卻是在NuPlayerDriver類中實現。
雖然如此,但這也不意味着new MediaPlayer()
會導致new NuPlayerDriver()
的函數調用。
不管是NuPlayer
還是NuPlayerDriver
實例的創建,都是在MediaPlayer
實例化後的setDataSource()
函數執行過程中實現的。
MediaPlayer
的調用代碼如下:
player = new MediaPlayer();
player.setDataSource(path);
至於具體調用過程就不分析了, 有興趣或者感到迷惑的同學可以去看一看MediaPlayer源碼分析就全明白了。來看一下setDataSource
部分的時序圖:
我們從最開始接觸到的NuPlayer世界的代碼開始,也就是status_t MediaPlayerService::Client::setDataSource(...)
函數。
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length) {
// 略掉一些對資源的判斷,剩下和可能和NuPlayer有關的部分
player_type playerType = MediaPlayerFactory::getPlayerType(this,
fd,
offset,
length);
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
// now set data source
return mStatus = setDataSource_post(p, p->setDataSource(fd, offset, length));
}
留下了三個比較重要的調用,先依次說一下它們的作用,然後再展開:
MediaPlayerFactory::getPlayerType
:該函數涉及Android底層媒體播放器的評分機制。通過評分,獲得一個最優的播放器類型,具體怎麼得到播放器類型,請閱:Android Framework層播放器評分機制,因爲AndroidO只剩下了NuPlayer
和TestPlayer
兩種播放器,TestPlayer
並未正式啓用。所以,函數調用返回的是NuPlayer
對應的播放器類型NU_PLAYER
。setDataSource_pre
:該函數的作用,是根據前面獲得的播放器類型創建播放器對象。setDataSource_post
:將媒體資源設置給播放器,這纔是真正的setDataSource
操作。
接下來展開:setDataSource_pre
和setDataSource_post
函數。
爲了讓小夥伴們看代碼片段的時候,可以有效的形成上下文邏輯,每部分代碼,都會配一張圖,以說明當前代碼所處位置,比如:現在我們在這裏(注意紅色箭頭,表示當前函數位置)。
NuPlayer播放器創建
前面已經提到,NuPlayer的創建過程,是在setDataSource_pre
函數中實現,我們接下來就展開一下吧:
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
player_type playerType)
{
// create the right type of player
sp<MediaPlayerBase> p = createPlayer(playerType);
// 刪掉了大量註冊服務監聽的代碼,包括extractor、IOMX
if (!p->hardwareOutput()) { // 播放器音頻是否通過硬件直接輸出,NuPlayer是不需要的。
Mutex::Autolock l(mLock);
mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(), mPid, mAudioAttributes);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
}
return p;
}
createPlayer
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
// 檢查當前進程,是否已經有一個播放器不同類型的播放器了,如果有,幹掉它
sp<MediaPlayerBase> p = mPlayer;
if ((p != NULL) && (p->playerType() != playerType)) {
ALOGV("delete player");
p.clear();
}
if (p == NULL) { // 創建對應類型的播放器。
p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid);
}
if (p != NULL) {
p->setUID(mUid);
}
return p;
}
這個函數最重要的部分是 MediaPlayerFactory::createPlayer
:
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
player_type playerType,
void* cookie,
notify_callback_f notifyFunc,
pid_t pid) {
sp<MediaPlayerBase> p;
IFactory* factory;
status_t init_result;
// 略掉一些非關鍵代碼
factory = sFactoryMap.valueFor(playerType);
p = factory->createPlayer(pid);
// 略掉一些非關鍵代碼
init_result = p->initCheck();
return p;
}
這個函數體實現的也比較簡單,邏輯如下:
sFactoryMap.valueFor
:通過sFactoryMap和playerType獲取播放器工廠對象。factory->createPlayer
:調用播放器工廠對象創建播放器對象。p->initCheck
:對播放器做初始化檢查。
sFactoryMap.valueFor
sFactoryMap是個什麼東西呢,看一下它的申明:
typedef KeyedVector<player_type, IFactory*> tFactoryMap;
static tFactoryMap sFactoryMap;
它是一個KeyedVector
的結構,以播放器類型爲鍵,對應的播放器工廠爲值。在MediaPlayerService
服務啓動時,會通過MediaPlayerFactory::registerBuiltinFactories()
函數調用,將所有的播放器工廠添加到這個Map結構中。這部分邏輯,在Android Framework層播放器評分機制一文中的註冊播放器工廠小節中詳細分析過,就不再贅述了。
我們已經知道此時的播放器類型爲NU_PLAYER,sFactoryMap.valueFor(playerType);
可以等價於:
sFactoryMap.valueFor(NU_PLAYER)
,所以,factory是NuPlayer播放器對應的工廠對象。簡單看一下類圖結構。
factory->createPlayer
通過類圖,和前面的分析,到這裏我們已經知道NuPlayer
的播放器工廠是NuPlayerFactory
類:
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
public:
// 刪掉了評分機制的代碼
virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
ALOGV(" create NuPlayer");
return new NuPlayerDriver(pid);
}
};
說好的創建NuPlayer
播放器呢,怎麼冒出來一個NuPlayerDriver
?
其實,雖然播放器叫NuPlayer
,但並意味着“播放器”只有NuPlayer
對象。實際上,NuPlayer
播放器由NuPlayerDriver
和NuPlayer
兩部分組成,NuPlayer
對象負責播放、暫停等功能函數的實現,NuPlayerDriver
則負責功能的調度,和MediaPlayerSerivce
等外界溝通。
回到代碼。
NuPlayerFactory::createPlayer
函數只new了一個NuPlayerDriver
,我們來看一下NuPlayerDriver
的初始化過程:
NuPlayerDriver::NuPlayerDriver(pid_t pid)
: mState(STATE_IDLE),
mLooper(new ALooper),
mPlayer(new NuPlayer(pid)),
mLooping(false),
mAutoLoop(false) {
mLooper->setName("NuPlayerDriver Looper");
mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_AUDIO);
mLooper->registerHandler(mPlayer);
mPlayer->setDriver(this);
}
爲了簡潔,代碼依然刪掉了不少暫時並不重要的。這部分代碼,其實我在Android媒體底層通信框架Native Handler(三):NuPlayer一文中已經講過。
有所不同的是,當時側重點放在媒體通信部分,也就是NativeHandler
邏輯部分。
NuPlayerDriver
的構造函數部分,除了NativeHandler
邏輯外,最重要的就是以下三個操作了:
mState(STATE_IDLE)
:將播放器狀態設置爲STATE_IDLE
(空閒)。new NuPlayer(pid)
:創建一個NuPlayer
對象,並讓NuPlayerDriver
持有NuPlayer
的引用。這裏稍後展開。setDriver(this)
:將NuPlayerDriver
設置給NuPlayer
,讓NuPlayer
持有NuPlayerDriver
的引用。
第二和第三點,讓NuPlayerDriver
和NuPlayer
相互持有引用,目的是在後續的流程控制中,方便彼此回調,配合工作。
到這裏,NuPlayer
的創建過程,算是明白了。NuPlayer
的構造函數沒什麼好看的,就是給一堆成員賦初值的過程。
initCheck()
因爲createPlayer
函數創建並返回的是NuPlayerDriver
對象,所以調用的是NuPlayerDriver::initCheck
函數:
status_t NuPlayerDriver::initCheck() {
return OK;
}
啥也沒幹,直接返回了OK
,有點浪費時間的感覺kugualian
代碼到哪兒了?
setAudioSink
AudioOutput
對象是音頻輸出的抽象層,在不支持硬件驅動直接輸出的接口下,需要手動設置音頻輸出的抽象層接口。
MediaPlayerBase
和它子類的結構圖如下:
通過createPlayer
函數,返回的是NuPlayerDriver
對象。
void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
mPlayer->setAudioSink(audioSink);
mAudioSink = audioSink;
}
這個調用的mPlayer
,在NuPlayerDriver
構造函數的初始化列表中,已經新建了一個NuPlayer
對象,並賦值給mPlayer
。所以,來看一下NuPlayer
的setAudioSink
:
void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
sp<AMessage> msg = new AMessage(kWhatSetAudioSink, this);
msg->setObject("sink", sink);
msg->post();
}
case kWhatSetAudioSink:
{
sp<RefBase> obj;
CHECK(msg->findObject("sink", &obj));
mAudioSink = static_cast<MediaPlayerBase::AudioSink *>(obj.get());
break;
}
關於
AMessage
、msg->findObject
等代碼和爲什麼這麼調用,可以去快速看一下Android媒體底層通信框架Native Handler(三):NuPlayer的總結部分。
可以看出,不管是NuPlayerDriver
還是NuPlayer
的setAudioSink
代碼,都是將新建的AudioOutput
對象存在對應的mAudioSink
字段中了,方便以後播放音頻做準備。
setDataSource
當前代碼位置:
在前一個流程中,創建了NuPlayer
和NuPlayerDriver
對象,並將NuPlayerDriver
對象指針保存在了p中,接着,通過p調用了NuPlayerDriver
的setDataSource
函數。
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
if (mState != STATE_IDLE) { // NuPlayerDriver構造中mState被設置成了STATE_IDLE。
return INVALID_OPERATION;
}
mState = STATE_SET_DATASOURCE_PENDING; // 將播放器狀態設置爲STATE_SET_DATASOURCE_PENDING
mPlayer->setDataSourceAsync(fd, offset, length); // 調用NuPlayer,設置媒體源
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock); // 加鎖,直到被通知喚醒
}
return mAsyncResult;
}
該函數主要作用:
- mState = STATE_SET_DATASOURCE_PENDING: 設置播放器狀態,和流程控制有關,比較重要,後面很多流程都需要判斷當前狀態,上一個狀態是
NuPlayerDriver
構造中設置的STATE_IDLE
狀態。 - mPlayer->setDataSourceAsync:實際上
NuPlayerDriver
並沒有處理資源的邏輯,前面也提到,它就是一層殼,需要將具體的動作交給NuPlayer
對象去做。 - while (mState == STATE_SET_DATASOURCE_PENDING):因爲上一步的
setDataSourceAsync
流程中會用到NativeHandler
機制,是異步的,所以在while循環體中加了一個鎖,讓當前線程阻塞。直到setDataSourceAsync
流程執行完畢後,喚醒。
setDataSourceAsync
繼續跟蹤setDataSourceAsync
函數:
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); // 新建消息,用於和解封裝模塊通信,類似於一種listener的功能。
sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID); // 創建解封裝器
status_t err = source->setDataSource(fd, offset, length); // 爲GenericSource設置媒體源
msg->setObject("source", source);
msg->post(); // 將創建並設置好的setDataSource,post給下一個流程處理
mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
}
該函數主要邏輯如下:
- new AMessage:構建了兩個消息對象,
msg
用於向下一個流程發送消息和當前函數執行的成果(source
)。notify
用於在構建GenericSource
的結果回調。 - new GenericSource:只是一個解封裝格式的類,同樣的類還有
RTSPSource
、HTTPLiveSource
等,是媒體流信息的直接處理者。媒體源信息也將被設置到該對象中。這會在一下篇文章進行展開,這裏就先留個疑問。 - source->setDataSource:將媒體流(源)設置給解封裝格式的解析器,這個也在下一篇文章中展開。
- msg->post():通過
NativeHandler
機制,將函數執行結果,也就是新創建的source
對象發送給下一個函數執行onMessageReceived
,這個過程是異步的,當前函數執行到這裏就會退棧。
創建了一個解封裝格式的解析器後,將結果post
到NuPlayer::onMessageReceived
函數處理:
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSetDataSource:
{
status_t err = OK;
sp<RefBase> obj;
CHECK(msg->findObject("source", &obj));
if (obj != NULL) {
Mutex::Autolock autoLock(mSourceLock);
mSource = static_cast<Source *>(obj.get()); // 將新創建的GenericSource對象,賦值給mSource
} else {
err = UNKNOWN_ERROR;
}
sp<NuPlayerDriver> driver = mDriver.promote();
if (driver != NULL) {
driver->notifySetDataSourceCompleted(err); // 通知NuPlayerDriver,任務完成
}
break;
}
// 略去一萬行代碼
}
}
這段代碼的重點在於:
- mSource =:將之前創建的GenericSource對象賦值給了mSource字段。
- driver->notifySetDataSourceCompleted:到這裏,整個setDataSource的流程已經執行完畢,函數調用回到NuPlayerDriver中。
NuPlayerDriver::notifySetDataSourceCompleted
void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) { // err = OK;
CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING); // 當前mState爲STATE_SET_DATASOURCE_PENDING
mAsyncResult = err;
mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE; // 將狀態設置爲STATE_UNPREPARED
mCondition.broadcast(); // 喚醒mCondition.wait(mLock);鎖,完成setDataSource函數調用
}
如果沒出以外,這裏的入參值應該是OK
的。所以,該函數的主要操作有:
- 將當前狀態設置成STATE_UNPREPARED。上一個狀態未STATE_SET_DATASOURCE_PENDING。
- mCondition.broadcast():發出喚醒mCondition鎖廣播。
釋放鎖後,NuPlayerDriver::setDataSource
會將執行的結果mAsyncResult
返回給調用者。setDataSource
流程執行完畢。