Android N Audio播放二:setDataSource窺探

  在上一篇Android如何播放一首音樂中介紹瞭如何使用MediaPlayer來播放音樂文件。可以看到,步驟非常簡單。

player.setDataSource(path);
player.prepare();
player.start();

  但如果你想更深入的瞭解Audio, 僅僅會調用這個幾個API是遠遠不夠的,沒有考慮容器格式,文件的來源,協議,編解碼等等。
  
  這篇文章就來介紹一下第一步的setDataSource到底都做了些什麼。還是以MusicDemo爲例,先將play方法中的prepare和start方法註釋掉, 這樣可以比較清楚的看到setDataSource的都幹了些什麼事。
  

    private void play(){
        try {
            String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/Music/Test.mp3";
            player.setDataSource(path);
            Log.d("Jaychou","MusicDemo setDataSource");
            //player.prepare();
            //Log.d("Jaychou","MusicDemo Prepare");
            //player.start();
        } catch (IOException e) {
            e.printStackTrace();
            Log.d("Jaychou","err when play");
        }
    }

先來一張流程圖:
這裏寫圖片描述

當點擊PLAY_MUSIC的時候,打出如下日誌:

02-10 15:45:13.538 V/MediaPlayer( 4748): setDataSource(59, 0, 576460752303423487)
02-10 15:45:13.540 V/MediaPlayerService(  647): Client(4) constructor
02-10 15:45:13.541 V/MediaPlayerService(  647): Create new client(4) from pid 4748, uid 10140, 
02-10 15:45:13.545 V/MediaPlayerService(  647): setDataSource fd=8, offset=0, length=576460752303423487
02-10 15:45:13.545 V/MediaPlayerService(  647): st_dev  = 21
02-10 15:45:13.545 V/MediaPlayerService(  647): st_mode = 33184
02-10 15:45:13.546 V/MediaPlayerService(  647): st_uid  = 0
02-10 15:45:13.546 V/MediaPlayerService(  647): st_gid  = 9997
02-10 15:45:13.546 V/MediaPlayerService(  647): st_size = 2932715
02-10 15:45:13.546 V/MediaPlayerService(  647): calculated length = 2932715
02-10 15:45:13.546 V/MediaPlayerService(  647): player type = 4
02-10 15:45:13.546 V/MediaPlayerFactory(  647):  create NuPlayer
02-10 15:45:13.547 V/NuPlayerDriver(  647): NuPlayerDriver(0xf39ab140)
02-10 15:45:13.551 V/AudioSink(  647): AudioOutput(57)
02-10 15:45:13.552 V/NuPlayerDriver(  647): setDataSource(0xf39ab140) file(8)
02-10 15:45:13.552 V/NuPlayer(  647): kWhatSetAudioSink
02-10 15:45:13.552 V/NuPlayer(  647): kWhatSetDataSource
02-10 15:45:13.553 I/ExtendedNuUtils(  647): printFileName fd(8) -> /storage/emulated/0/Music/Test.mp3
02-10 15:45:13.553 V/MediaPlayerService(  647):  setDataSource
02-10 15:45:13.555 D/Jaychou ( 4748): MusicDemo setDataSource

結合流程圖和Log, 我們來看一下setDataSource做的事情。

1、MusicDemo調用framework MediaPlayer.java的接口,繼而通過JNI(JNI我這裏沒有畫出來)再調用到Mediaplayer.cpp, 然後再通過Binder(IMediaPlayer)調到MediaPlayerService. 可以看到,MusicDemo, MediaPlayer.java, Mediaplayer.cpp, IMediaPlayer屬於一個進程,pid是4748.
這裏寫圖片描述

2、MediaPlayerService的setDataSource主要有三個動作:

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource fd=%d, offset=%" PRId64 ", length=%" PRId64 "", fd, offset, length);
    struct stat sb;
    int ret = fstat(fd, &sb);
    if (ret != 0) {
        ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
        return UNKNOWN_ERROR;
    }

    ALOGV("st_dev  = %" PRIu64 "", static_cast<uint64_t>(sb.st_dev));
    ALOGV("st_mode = %u", sb.st_mode);
    ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));
    ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));
    ALOGV("st_size = %" PRId64 "", sb.st_size);

    if (offset >= sb.st_size) {
        ALOGE("offset error");
        return UNKNOWN_ERROR;
    }
    if (offset + length > sb.st_size) {
        length = sb.st_size - offset;
        ALOGV("calculated length = %" PRId64 "\n", length);
    }

    player_type playerType = MediaPlayerFactory::getPlayerType(this,
                                                               fd,
                                                               offset,
                                                               length);
    sp<MediaPlayerBase> p = setDataSource_pre(playerType);
    if (p == NULL) {
        return NO_INIT;
    }

    // now set data source
    setDataSource_post(p, p->setDataSource(fd, offset, length));
    return mStatus;
}

A: 獲得播放器類型

MediaPlayerFactory::getPlayerType

從Log看是Nuplayer, Android N上android已經完全拋棄了Asomeplayer, 源代碼裏面已經看不到相關的代碼了。

B: 創建播放器,設置AudioSink等。

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType)
{
    ALOGV("player type = %d", playerType);

    // create the right type of player
    sp<MediaPlayerBase> p = createPlayer(playerType);
    if (p == NULL) {
        return p;
    }

    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->getService(String16("media.extractor"));
    if (binder == NULL) {
        ALOGE("Unable to connect to media extractor service");
        return NULL;
    }

    mExtractorDeathListener = new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH);
    binder->linkToDeath(mExtractorDeathListener);

    binder = sm->getService(String16("media.codec"));
    if (binder == NULL) {
        ALOGE("Unable to connect to media codec service");
        return NULL;
    }

    mCodecDeathListener = new ServiceDeathNotifier(binder, p, MEDIACODEC_PROCESS_DEATH);
    binder->linkToDeath(mCodecDeathListener);

    if (!p->hardwareOutput()) {
        Mutex::Autolock l(mLock);
        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
                mPid, mAudioAttributes);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
    }

    return p;
}

C: 調用NuplayerDriver的setDataSource

p->setDataSource(fd, offset, length)

3、setDataSource是一個Blocking的方法, 在NuplayerDriver中可以看出,

status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
    ALOGV("setDataSource(%p) file(%d)", this, fd);
    Mutex::Autolock autoLock(mLock);

    if (mState != STATE_IDLE) {
        return INVALID_OPERATION;
    }

    mState = STATE_SET_DATASOURCE_PENDING;

    mPlayer->setDataSourceAsync(fd, offset, length);

    while (mState == STATE_SET_DATASOURCE_PENDING) {
        mCondition.wait(mLock);
    }

    AVNuUtils::get()->printFileName(fd);
    return mAsyncResult;
}

設置source之前狀態是STATE_SET_DATASOURCE_PENDING, 當設置完成以後,NuplayerDriver的notifySetDataSourceCompleted會被調用, 狀態更新成STATE_UNPREPARED。

4、NuplayerDriver調用的是Nuplayer的setDataSourceAsync, 這裏通過構造GenericSource將數據源抽成了source。這個source就供下一步的解複用(demux)來使用。

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

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

這樣,整個setDataSource的流程就走完了。

這裏寫圖片描述

發佈了15 篇原創文章 · 獲贊 17 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章