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); }
共有三個步驟:
- 構建AudioTrack對象,並且把PCM的參數傳到對象里面
- 調用start
- 調用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*/);
除了對參數的預處理以及保存之外,中間最主要的功能有三個:
- getOutput,獲取輸出設備並打開該設備
- 創建AudioTrackThread,並執行該線程
- 創建音軌,實際上是創建用戶與Android系統的共享內存,用於傳輸音頻數據
由於getOutput與createTrack_l的篇幅較大,我會在后面的章節在分析,這里分析一下AudioTrackThread。
AudioTrackThread主要用於反饋處理,會根據不同的buffer狀態進行不同的操作,其中buffer狀態包括下面幾種:
- buffer的position有增長,說明buffer正在被填充;
- remainFrames減少說明buffer正在被消耗,即buffer內的Audio數據已被混音;
- loop_cycle說明buffer需要循環播放;
- loop_final說明buffer已經播放完畢;
- 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數據流動的第一步。