[Android] AudioTrack實例


AudioTrack在Android系統中是用於PCM數據的混音、播放,並不涉及到音頻的解碼。因此MP3這類經過編碼的音頻格式文件不能直接通過AudioTrack正確地播放,AudioTrack只能播放PCM格式的音頻數據,如wav格式的音頻。

 

AudioTrack播放音頻的實例如下:

    AudioTrack audio = new AudioTrack(
                           AudioManager.STREAM_MUSIC, // stream mode
                           32000, // sample rate
                           AudioFormat.CHANNEL_OUT_STEREO, // single or stereo
                           AudioFormat.ENCODING_PCM_16BIT, // bit format
                           AudioTrack.MODE_STREAM
                       );
    audio.start();
    byte[] buffer = new buffer[4096];
    int count;
    while(true)
    {
        audio.write(buffer, 0, 4096);
    }

 

 

共有三個步驟:

  1. 構建AudioTrack對象,並且把PCM的參數傳到對象里面
  2. 調用start
  3. 調用write

 

 

 

1. 構建AudioTrack對象

在AudioTrack構造方法內部僅調用了一個set函數

AudioTrack::AudioTrack(
        audio_stream_type_t streamType,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        const sp<IMemory>& sharedBuffer,
        audio_output_flags_t flags,
        callback_t cbf,
        void* user,
        int notificationFrames,
        int sessionId,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        int uid)
    : mStatus(NO_INIT),
      mIsTimed(false),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT)
{
    mStatus = set(streamType, sampleRate, format, channelMask,
            0 /*frameCount*/, flags, cbf, user, notificationFrames,
            sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, uid);
}

 

 

set方法內部主要做了什么?

status_t AudioTrack::set(
        audio_stream_type_t streamType,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        int frameCountInt,
        audio_output_flags_t flags,
        callback_t cbf,
        void* user,
        int notificationFrames,
        const sp<IMemory>& sharedBuffer,
        bool threadCanCallJava,
        int sessionId,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        int uid)
{

    audio_io_handle_t output = AudioSystem::getOutput(
                                    streamType,
                                    sampleRate, format, channelMask,
                                    flags,
                                    offloadInfo);


    if (cbf != NULL) {
        mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
        mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
    }


    // create the IAudioTrack
    status_t status = createTrack_l(streamType,
                                  sampleRate,
                                  format,
                                  frameCount,
                                  flags,
                                  sharedBuffer,
                                  output,
                                  0 /*epoch*/);


除了對參數的預處理以及保存之外,中間最主要的功能有三個:

  1. getOutput,獲取輸出設備並打開該設備
  2. 創建AudioTrackThread,並執行該線程
  3. 創建音軌,實際上是創建用戶與Android系統的共享內存,用於傳輸音頻數據

 

 

由於getOutput與createTrack_l的篇幅較大,我會在后面的章節在分析,這里分析一下AudioTrackThread。

 

AudioTrackThread主要用於反饋處理,會根據不同的buffer狀態進行不同的操作,其中buffer狀態包括下面幾種:

  1. buffer的position有增長,說明buffer正在被填充;
  2. remainFrames減少說明buffer正在被消耗,即buffer內的Audio數據已被混音;
  3. loop_cycle說明buffer需要循環播放;
  4. loop_final說明buffer已經播放完畢;
  5. etc.

 

對於不同的狀態,會調用到回調函數mCbf,該函數在set的時候被傳入。回調函數mCbf的作用還是很大的,比如說能通知應用程序當前的播放狀態,也能根據當前的狀態繼續進行下一步的操作。像AwesomePlayer中的音頻處理模塊AudioPlayer就能通過mCbf的回調自動填充buffer。

bool AudioTrack::AudioTrackThread::threadLoop()
{

    nsecs_t ns = mReceiver.processAudioBuffer(this);

}



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

nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
{

        mCbf(...);

}

 

2. 調用start

start方法是觸發混音線程對當前音軌進行混音並輸出,篇幅較大,會在后面章節分析

 

 

3. 調用write

write方法需要獲得剛剛所創建的音軌,並且把PCM數據往音軌寫入。由於音軌的大小有限,write也很有可能一次性不能寫入全部數據,因此需要循環調用write方法

 

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

ssize_t AudioTrack::write(const void* buffer, size_t userSize)
{

    while (userSize >= mFrameSize) {
        audioBuffer.frameCount = userSize / mFrameSize;

        status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever);

        memcpy(audioBuffer.i8, buffer, toWrite);
    }
}

 

 

需要注意的就是AudioTrack除了初始化之外,write只是往音軌內寫入PCM數據,這是Audio數據流動的第一步。

 

 

 

 

AudioTrack


免責聲明!

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



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