2025-02-10

打算以測試代碼中所使用的接口為點,以接口間調用關系為線,逐步撕開Android中Audio的面紗。


*****************************************源碼*************************************************
    public void testPlaybackHeadPositionAfterInit() throws Exception {
        // constants for test
        final String TEST_NAME = "testPlaybackHeadPositionAfterInit";
        final int TEST_SR = 22050;
        final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
        final int TEST_MODE = AudioTrack.MODE_STREAM;
        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
       
        //——– initialization ————–
        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
                AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT), TEST_MODE);
        //——–    test        ————–
        assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
        assertTrue(TEST_NAME, track.getPlaybackHeadPosition() == 0);
        //——– tear down      ————–
        track.release();
    }
**********************************************************************************************
源碼路徑:
frameworks\base\media\tests\mediaframeworktest\src\com\android\mediaframeworktest\functional\MediaAudioTrackTest.java


#######################說明################################
    //Test case 1: getPlaybackHeadPosition() at 0 after initialization
    public void testPlaybackHeadPositionAfterInit() throws Exception {
        // constants for test
        final String TEST_NAME = "testPlaybackHeadPositionAfterInit";
        final int TEST_SR = 22050;
        final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
        final int TEST_MODE = AudioTrack.MODE_STREAM;
        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
       
        //——– initialization ————–
        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
                AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT), TEST_MODE);
        //——–    test        ————–
        assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
// 今天要看的,就是下面的getPlaybackHeadPosition函數
// 由於在getPlaybackHeadPosition之前,並沒有調用play函數,也就是沒有開始播放,所以獲取的position應該為0
        assertTrue(TEST_NAME, track.getPlaybackHeadPosition() == 0);
+++++++++++++++++++++++++++++++getPlaybackHeadPosition+++++++++++++++++++++++++++++++++
    /**
     * Returns the playback head position expressed in frames
     */
    public int getPlaybackHeadPosition() {
// 很直接
// 調用的是函數android_media_AudioTrack_get_position
        return native_get_position();
++++++++++++++++++++++++++++++android_media_AudioTrack_get_position++++++++++++++++++++++++++++++++++
路徑:frameworks\base\core\jni\android_media_AudioTrack.cpp


static jint android_media_AudioTrack_get_position(JNIEnv *env,  jobject thiz) {
   
    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    uint32_t position = 0;
               
    if (lpTrack) {
        lpTrack->getPosition(&position);
        return (jint)position;
++++++++++++++++++++++++++++++++AudioTrack::getPosition++++++++++++++++++++++++++++++++
status_t AudioTrack::getPosition(uint32_t *position)
{
    if (position == 0) return BAD_VALUE;


    *position = mCblk->server;


    return NO_ERROR;
}


server是mCblk的成員變量,mCblk是audio_track_cblk_t對象。
server在函數audio_track_cblk_t::stepServer中有被賦值。
另外,mCblk->server有在函數AudioTrack::setPosition中被賦值。
+++++++++++++++++++++++++++++++++AudioTrack::setPosition+++++++++++++++++++++++++++++++
status_t AudioTrack::setPosition(uint32_t position)
{
    Mutex::Autolock _l(mCblk->lock);


    if (!stopped()) return INVALID_OPERATION;


    if (position > mCblk->user) return BAD_VALUE;


    mCblk->server = position;
    mCblk->flags |= CBLK_FORCEREADY_ON;


    return NO_ERROR;
}


android_media_AudioTrack.cpp文件中的以下兩個函數調用瞭AudioTrack::setPosition:
android_media_AudioTrack_set_pos_update_period函數
android_media_AudioTrack_set_position函數
這些接口上給java層用的。
想象一下使用場景,拖動當前光標?
———————————AudioTrack::setPosition——————————-
+++++++++++++++++++++++++++++++++audio_track_cblk_t::stepServer+++++++++++++++++++++++++++++++
bool audio_track_cblk_t::stepServer(uint32_t frameCount)
{
    // the code below simulates lock-with-timeout
    // we MUST do this to protect the AudioFlinger server
    // as this lock is shared with the client.
    status_t err;


    err = lock.tryLock();
    if (err == -EBUSY) { // just wait a bit
        usleep(1000);
        err = lock.tryLock();
    }
    if (err != NO_ERROR) {
        // probably, the client just died.
        return false;
    }


    uint64_t s = this->server;


    s += frameCount;
    if (flags & CBLK_DIRECTION_MSK) {
        // Mark that we have read the first buffer so that next time stepUser() is called
        // we switch to normal obtainBuffer() timeout period
        if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) {
            bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS – 1;
        }
        // It is possible that we receive a flush()
        // while the mixer is processing a block: in this case,
        // stepServer() is called After the flush() has reset u & s and
        // we have s > u
        if (s > this->user) {
            LOGW("stepServer occured after track reset");
            s = this->user;
        }
    }


    if (s >= loopEnd) {
        LOGW_IF(s > loopEnd, "stepServer: s %llu > loopEnd %llu", s, loopEnd);
        s = loopStart;
        if (–loopCount == 0) {
            loopEnd = ULLONG_MAX;
            loopStart = ULLONG_MAX;
        }
    }
    if (s >= serverBase + this->frameCount) {
        serverBase += this->frameCount;
    }


    this->server = s;


    cv.signal();
    lock.unlock();
    return true;
}


函數AudioFlinger::ThreadBase::TrackBase::step中調用瞭函數audio_track_cblk_t::stepServer。
++++++++++++++++++++++++++++++AudioFlinger::ThreadBase::TrackBase::step++++++++++++++++++++++++++++++++++
bool AudioFlinger::ThreadBase::TrackBase::step() {
    bool result;
    audio_track_cblk_t* cblk = this->cblk();


    result = cblk->stepServer(mFrameCount);
    if (!result) {
        LOGV("stepServer failed acquiring cblk mutex");
        mFlags |= STEPSERVER_FAILED;
    }
    return result;
}


函數AudioFlinger::PlaybackThread::Track::getNextBuffer和函數AudioFlinger::RecordThread::RecordTrack::getNextBuffer
調用瞭函數AudioFlinger::ThreadBase::TrackBase::step。
我們這兒隻看函數AudioFlinger::PlaybackThread::Track::getNextBuffer。
+++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::Track::getNextBuffer+++++++++++++++++++++++++++++++++++
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{
     audio_track_cblk_t* cblk = this->cblk();
     uint32_t framesReady;
     uint32_t framesReq = buffer->frameCount;


     // Check if last stepServer failed, try to step now
     if (mFlags & TrackBase::STEPSERVER_FAILED) {
         if (!step())  goto getNextBuffer_exit;
         LOGV("stepServer recovered");
         mFlags &= ~TrackBase::STEPSERVER_FAILED;
     }


     framesReady = cblk->framesReady();


     if (LIKELY(framesReady)) {
        uint64_t s = cblk->server;
        uint64_t bufferEnd = cblk->serverBase + cblk->frameCount;


        bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd;
        if (framesReq > framesReady) {
            framesReq = framesReady;
        }
        if (s + framesReq > bufferEnd) {
            framesReq = bufferEnd – s;
        }


         buffer->raw = getBuffer(s, framesReq);
         if (buffer->raw == 0) goto getNextBuffer_exit;


         buffer->frameCount = framesReq;
        return NO_ERROR;
     }


getNextBuffer_exit:
     buffer->raw = 0;
     buffer->frameCount = 0;
     LOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
     return NOT_ENOUGH_DATA;
}


該函數在看framesReady的時候已經看過瞭,此時就不再多說瞭。
—————————–AudioFlinger::PlaybackThread::Track::getNextBuffer———————————–
——————————AudioFlinger::ThreadBase::TrackBase::step———————————-
———————————audio_track_cblk_t::stepServer——————————-
——————————–AudioTrack::getPosition——————————–
    }  else {
        jniThrowException(env, "java/lang/IllegalStateException",
            "Unable to retrieve AudioTrack pointer for getPosition()");
        return AUDIOTRACK_ERROR;
    }
}
——————————android_media_AudioTrack_get_position———————————-
    }
——————————-getPlaybackHeadPosition———————————
        //——– tear down      ————–
        track.release();
    }
###########################################################


&&&&&&&&&&&&&&&&&&&&&&&總結&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
get position其實就是取得audio_track_cblk_t對象中server的位置。
server的位置在set position或者step server的時候會被改變。
應用程序會set position。
Get next buffer的時候會step server。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

摘自:江風的專欄

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *