NuPlayer源碼分析一:播放器創建

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));
}

留下了三個比較重要的調用,先依次說一下它們的作用,然後再展開:

  1. MediaPlayerFactory::getPlayerType:該函數涉及Android底層媒體播放器的評分機制。通過評分,獲得一個最優的播放器類型,具體怎麼得到播放器類型,請閱:Android Framework層播放器評分機制,因爲AndroidO只剩下了NuPlayerTestPlayer兩種播放器,TestPlayer並未正式啓用。所以,函數調用返回的是NuPlayer對應的播放器類型NU_PLAYER
  2. setDataSource_pre:該函數的作用,是根據前面獲得的播放器類型創建播放器對象。
  3. setDataSource_post:將媒體資源設置給播放器,這纔是真正的setDataSource操作。

接下來展開:setDataSource_presetDataSource_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播放器由NuPlayerDriverNuPlayer兩部分組成,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邏輯外,最重要的就是以下三個操作了:

  1. mState(STATE_IDLE):將播放器狀態設置爲STATE_IDLE(空閒)。
  2. new NuPlayer(pid):創建一個NuPlayer對象,並讓NuPlayerDriver持有NuPlayer的引用。這裏稍後展開。
  3. setDriver(this):將NuPlayerDriver設置給NuPlayer,讓NuPlayer持有NuPlayerDriver的引用。

第二和第三點,讓NuPlayerDriverNuPlayer相互持有引用,目的是在後續的流程控制中,方便彼此回調,配合工作。

到這裏,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。所以,來看一下NuPlayersetAudioSink

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;
        }

關於AMessagemsg->findObject等代碼和爲什麼這麼調用,可以去快速看一下Android媒體底層通信框架Native Handler(三):NuPlayer的總結部分。

可以看出,不管是NuPlayerDriver還是NuPlayersetAudioSink代碼,都是將新建的AudioOutput對象存在對應的mAudioSink字段中了,方便以後播放音頻做準備。

setDataSource

當前代碼位置:

在這裏插入圖片描述

在前一個流程中,創建了NuPlayerNuPlayerDriver對象,並將NuPlayerDriver對象指針保存在了p中,接着,通過p調用了NuPlayerDriversetDataSource函數。

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:只是一個解封裝格式的類,同樣的類還有RTSPSourceHTTPLiveSource等,是媒體流信息的直接處理者。媒體源信息也將被設置到該對象中。這會在一下篇文章進行展開,這裏就先留個疑問。
  • source->setDataSource:將媒體流(源)設置給解封裝格式的解析器,這個也在下一篇文章中展開。
  • msg->post():通過NativeHandler機制,將函數執行結果,也就是新創建的source對象發送給下一個函數執行onMessageReceived,這個過程是異步的,當前函數執行到這裏就會退棧。

創建了一個解封裝格式的解析器後,將結果postNuPlayer::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流程執行完畢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章