[Android] AudioTrack::start


AudioTrack的start方法用於實現Android的音頻輸出,start究竟做了什么?回顧一下上一小節createTrack_l的最后部分,通過binder返回了一個Track的句柄,並以被保存了下來

status_t AudioTrack::createTrack_l(...)
{
    sp<IAudioTrack> track = audioFlinger->createTrack(...);
    mAudioTrack = track;
}

start主要就是調用這個track的start方法實現音頻輸出功能的

// -------------------------------------------------------------------------

status_t AudioTrack::start()
{
    AutoMutex lock(mLock);
    //如果該AudioTrack已經是start狀態,直接返回
    if (mState == STATE_ACTIVE) {
        return INVALID_OPERATION;
    }

    mInUnderrun = true;
    //保存上一次的狀態
    State previousState = mState;
    //設置當前狀態
    if (previousState == STATE_PAUSED_STOPPING) {
        mState = STATE_STOPPING;
    } else {
        mState = STATE_ACTIVE;
    }
    //如果上一狀態是停止狀態,表明需要重新把position設置為0,從頭播放
    if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
        // reset current position as seen by client to 0
        mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
        // force refresh of remaining frames by processAudioBuffer() as last
        // write before stop could be partial.
        mRefreshRemaining = true;
    }
    //當前位置
    mNewPosition = mProxy->getPosition() + mUpdatePeriod;
    //獲取share buffer的flag,原子操作
    int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags);

    //是否有回調線程,一般如果我們在apk端獨立調用AudioTrack,是不會設置回調線程的,但是AudioPlayer這種系統播放器則會設置回調線程
    //這樣做是為了設置優先級,否則Audio可能會由於得不到時間片,而卡頓
    //如果是AudioPlayer,會有自己定義的優先級,AudioTrack后面新創建的線程則會繼承它的優先級
    //如果是Apk調用,優先級一般都是固定的,那么我們需要在這里設置一個ANDROID_PRIORITY_AUDIO的優先級來保證Audio的流暢輸出
    sp<AudioTrackThread> t = mAudioTrackThread;
    if (t != 0) {
        if (previousState == STATE_STOPPING) {
            //中斷
            mProxy->interrupt();
        } else {
            //恢復播放
            t->resume();
        }
    } else {
        //保存當前線程優先級,在后面停止的時候設置回來
        mPreviousPriority = getpriority(PRIO_PROCESS, 0);
        get_sched_policy(0, &mPreviousSchedulingGroup);
        //設置線程優先級為ANDROID_PRIORITY_AUDIO
        androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
    }

    status_t status = NO_ERROR;
    if (!(flags & CBLK_INVALID)) {
        //如果share buffer可用,則調用track的start方法
        status = mAudioTrack->start();
        if (status == DEAD_OBJECT) {
            flags |= CBLK_INVALID;
        }
    }
    if (flags & CBLK_INVALID) {
        status = restoreTrack_l("start");
    }

    if (status != NO_ERROR) {
        //start出錯后的處理
        ALOGE("start() status %d", status);
        mState = previousState;
        if (t != 0) {
            if (previousState != STATE_STOPPING) {
                t->pause();
            }
        } else {
            setpriority(PRIO_PROCESS, 0, mPreviousPriority);
            set_sched_policy(0, mPreviousSchedulingGroup);
        }
    }

    return status;
}

由於mAudioTrack是binder的proxy對象,因此start會調用到BBinder對象的start方法,即

status_t AudioFlinger::TrackHandle::start() {
    return mTrack->start();
}

 

由於我們是在PlaybackThread下進行音頻輸出的,因此會進一步調用到PlaybackThread::Track:: start方法,其中最主要的是下面兩個步驟:

status_t AudioFlinger::PlaybackThread::Track::start(
        PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
        status = playbackThread->addTrack_l(this);
}

還記得我們在getOutput的時候創建了一個MixerThread嗎,而且在createTrack_l的時候把這個Thread加入了mPlaybackThreads進行管理,現在我們要把它取出來,調用它的addTrack_l方法了

audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,...)
{     thread = new MixerThread(this, output, id, *pDevices);     return id;
}

AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const
{     return mPlaybackThreads.valueFor(output).get();
}

 

在addTrack_l方法內,主要步驟有三個:

  • 如果該track(share buffer)是新增track,則需要調用startOutput進行初始化
  • 把該track加入mActiveTracks
  • 發送廣播,通知MixerThread開始工作
// addTrack_l() must be called with ThreadBase::mLock held
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
    if (mActiveTracks.indexOf(track) < 0) {
        status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId());
    }
    mActiveTracks.add(track);

    broadcast_l();
}

 

1. track初始化

在分析getOutput的時候,我們已經知道Audio接口的調用流程,即AudioSystem->AudioPolicyService->Audio_policy_hal->AudioPolicyManagerBase,現在我們來看一下AudioPolicyManagerBase:: startOutput做了什么

status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
                                             AudioSystem::stream_type stream,
                                             int session)
{
        checkAndSetVolume(stream,
                          mStreams[stream].getVolumeIndex(newDevice),
                          output,
                          newDevice);
}

checkAndSetVolume其實只是設置了stream volume.

 

2.  track加入mAudioTrack

mAudioTrack即當前MixerThread所包含的Track集合,在后面就是對這些Track集合進行混音

 

 

3. broadcast_l

void AudioFlinger::PlaybackThread::broadcast_l()
{
    // Thread could be blocked waiting for async
    // so signal it to handle state changes immediately
    // If threadLoop is currently unlocked a signal of mWaitWorkCV will
    // be lost so we also flag to prevent it blocking on mWaitWorkCV
    mSignalPending = true;
    mWaitWorkCV.broadcast();
}

我們已經有了MixerThread,由於MixerThread繼承與PlaybackThread,因此跑的是PlaybackThread::threadLoop,在threadLoop內,如果mActiveTrack為空的話,表明沒有音頻數據等待輸出,那么threadLoop會進入睡眠,等待喚醒,這里的broadcast就是做了這個喚醒的工作

bool AudioFlinger::PlaybackThread::threadLoop()
{
    if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
                                   isSuspended())

        mWaitWorkCV.wait(mLock);

    }
    ...
}

 

下面是start的總體流程

Audio_start


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM