AudioTrack是Android中比較偏底層的用來播放音頻的接口,它主要被用來播放PCM音頻數據,和MediaPlayer不同,它不涉及到文件解析和解碼等復雜的流程,比較適合通過它來分析Android系統播放音頻數據的過程。下面是https://developer.android.com/reference/android/media/AudioTrack.html 對AudioTrank的描述:
1.應用層使用AudioTrack播放PCM音頻數據
//MainActivity.java import android.app.Activity; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.AudioTrack; import android.media.MediaRecorder; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.widget.Button; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class MainActivity extends Activity { private static final String TAG = "VoiceRecord"; private static final int RECORDER_SAMPLERATE = 8000; private static final int RECORDER_CHANNELS_IN = AudioFormat.CHANNEL_IN_MONO; private static final int RECORDER_CHANNELS_OUT = AudioFormat.CHANNEL_OUT_MONO; private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT; private static final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC; // Initialize minimum buffer size in bytes. private int bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS_IN, RECORDER_AUDIO_ENCODING); private AudioRecord recorder = null; private Thread recordingThread = null; private boolean isRecording = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((Button) findViewById(R.id.start_button)).setOnClickListener(btnClick); ((Button) findViewById(R.id.stop_button)).setOnClickListener(btnClick); enableButtons(false); } private void enableButton(int id, boolean isEnable) { ((Button) findViewById(id)).setEnabled(isEnable); } private void enableButtons(boolean isRecording) { enableButton(R.id.start_button, !isRecording); enableButton(R.id.stop_button, isRecording); } private void startRecording() { if( bufferSize == AudioRecord.ERROR_BAD_VALUE) Log.e(TAG, "Bad Value for \"bufferSize\", recording parameters are not supported by the hardware"); if( bufferSize == AudioRecord.ERROR ) Log.e( TAG, "Bad Value for \"bufferSize\", implementation was unable to query the hardware for its output properties"); Log.e( TAG, "\"bufferSize\"="+bufferSize); // Initialize Audio Recorder. recorder = new AudioRecord(AUDIO_SOURCE, RECORDER_SAMPLERATE, RECORDER_CHANNELS_IN, RECORDER_AUDIO_ENCODING, bufferSize); // Starts recording from the AudioRecord instance. recorder.startRecording(); isRecording = true; recordingThread = new Thread(new Runnable() { public void run() { writeAudioDataToFile(); } }, "AudioRecorder Thread"); recordingThread.start(); } private void writeAudioDataToFile() { //Write the output audio in byte String filePath = "/sdcard/8k16bitMono.pcm"; byte saudioBuffer[] = new byte[bufferSize]; FileOutputStream os = null; try { os = new FileOutputStream(filePath); } catch (FileNotFoundException e) { e.printStackTrace(); } while (isRecording) { // gets the voice output from microphone to byte format recorder.read(saudioBuffer, 0, bufferSize); try { // writes the data to file from buffer stores the voice buffer os.write(saudioBuffer, 0, bufferSize); } catch (IOException e) { e.printStackTrace(); } } try { os.close(); } catch (IOException e) { e.printStackTrace(); } } private void stopRecording() throws IOException { // stops the recording activity if (null != recorder) { isRecording = false; recorder.stop(); recorder.release(); recorder = null; recordingThread = null; PlayShortAudioFileViaAudioTrack("/sdcard/8k16bitMono.pcm"); } } private void PlayShortAudioFileViaAudioTrack(String filePath) throws IOException{ // We keep temporarily filePath globally as we have only two sample sounds now.. if (filePath==null) return; //Reading the file.. File file = new File(filePath); // for ex. path= "/sdcard/samplesound.pcm" or "/sdcard/samplesound.wav" byte[] byteData = new byte[(int) file.length()]; Log.d(TAG, (int) file.length()+""); FileInputStream in = null; try { in = new FileInputStream( file ); in.read( byteData ); in.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } // Set and push to audio track.. int intSize = android.media.AudioTrack.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS_OUT, RECORDER_AUDIO_ENCODING); Log.d(TAG, intSize+""); AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, RECORDER_SAMPLERATE, RECORDER_CHANNELS_OUT, RECORDER_AUDIO_ENCODING, intSize, AudioTrack.MODE_STREAM); if (at!=null) { at.play(); // Write the byte array to the track at.write(byteData, 0, byteData.length); at.stop(); at.release(); } else Log.d(TAG, "audio track is not initialised "); } private View.OnClickListener btnClick = new View.OnClickListener() { public void onClick(View v) { switch (v.getId()) { case R.id.start_button: { enableButtons(true); startRecording(); break; } case R.id.stop_button: { enableButtons(false); try { stopRecording(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } break; } } } }; // onClick of backbutton finishes the activity. @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { finish(); } return super.onKeyDown(keyCode, event); } }
<!--activity_main.xml--> <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/start_button" android:text="start"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/stop_button" android:text="stop"/> </LinearLayout>
在AndroidManifest.xml中添加權限
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
通過上面的例子可以看出,使用AudioTrack播放PCM數據一般的步驟為:
(1)根據音頻數據的特性來確定所要分配的緩沖區的最小size
int intSize = android.media.AudioTrack.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS_OUT, RECORDER_AUDIO_ENCODING);
其中第一個參數為采樣率,第二個參數為聲道數,第三個參數為采樣精度
(2)創建AudioTrack
AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC,RECORDER_SAMPLERATE,RECORDER_CHANNELS_OUT, RECORDER_AUDIO_ENCODING, intSize, AudioTrack.MODE_STREAM);
第一個參數為音頻流類型,音頻流類型的划分和Audio系統對音頻的管理策略有關。最后一個參數為數據加載模式分為MODE_STREAM和MODE_STATIC兩種:
MODE_STREAM:通過 write 一次次把音頻數據寫到 AudioTrack 中。這種工作方式每次都需要把數據從用戶提供的 buffer 拷貝到 AudioTrack 內部 buffer。
MODE_STATIC:在 play 之前只需要把數據通過一次 write 調用傳遞到 AudioTrack 內部 buffer。適用於內存占用量小,延遲要求高的文件。
(3)開始播放
at.play();
(4)循環往AudioTrack中寫數據
at.write(byteData, 0, byteData.length);
(5)停止播放和釋放資源
at.stop();
at.release();
2.Framework層AudioTrack分析
上面我們分析了應用層使用AudioTrack來播放PCM音頻數據的主要步驟,所以接下來就來分析一下各個步驟中Android Framework層都做了些什么,以及每一個步驟是怎么一步步調用到底層去的。
2.1創建AudioTrack
在上面應用層使用AudioTrack播放PCM數據的例子里,我們首先根據給出的音頻參數通過 android.media.AudioTrack.getMinBufferSize 方法獲得了需要分配的最小的緩沖區大小。然后創建了一個AudioTrack對象。整個打開output的流程如下圖所示。
在創建AudioTrack對象的時候,不管調用的那個構造方法,最終都會調用到
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int mode, int sessionId)
這個構造方法,下面就以這個構造方法為切入點來分析AudioTrack對象的構造過程。
// frameworks/base/media/java/android/media/AudioTrack.java public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int mode, int sessionId) throws IllegalArgumentException { .... //調用JNI函數 int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, mSampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode, session); .... }
在這個構造函數中,首先對傳入的參數進行了處理,然后調用了JNI層native_setup()函數,我們就根據這條主線繼續跳到JNI層進行分析。
// frameworks/base/core/jni/android_media_AudioTrack.cpp static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask, jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) { ...... // create the native AudioTrack object sp<AudioTrack> lpTrack = new AudioTrack(); //生成一個Storage對象用來存儲音頻數據 // initialize the callback information: // this data will be passed with every AudioTrack callback AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); .... //調用native AudioTrack的set函數!!! status = lpTrack->set( // stream type, but more info conveyed in paa (last argument) AUDIO_STREAM_DEFAULT, sampleRateInHertz, format,// word length, PCM nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE, //回調函數 回調數據 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) 0, lpJniStorage->mMemBase,// shared mem true,// thread can call Java sessionId,// audio session ID AudioTrack::TRANSFER_SYNC, NULL, // default offloadInfo -1, -1, // default uid, pid values paa); ...... }
JNI層函數native_setup創建了一個AudioTrack(native)對象,然后進行各種屬性的計算,最后調用AudioTrack(native)對象的set()函數來設置這些屬性,對於兩種不同的內存模式(MODE_STATIC和MODE_STREAM有不同的處理方式:
對於靜態方式set的倒數第三個參數是lpJniStorage->mMemBase,而對於STREAM類型這個參數為null(0)。
首先來看一下native層AudioTrack的無參構造函數
// frameworks/av/media/libmedia/AudioTrack.cpp AudioTrack::AudioTrack() : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), mPausedPosition(0), mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) { mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN; mAttributes.usage = AUDIO_USAGE_UNKNOWN; mAttributes.flags = 0x0; strcpy(mAttributes.tags, ""); }
可以看出來,在這個構造函數里面只是對參數進行了處理,並沒有我們想要看到的那些流程,接下來就來分析這個set()函數。
// frameworks/av/media/libmedia/AudioTrack.cpp status_t AudioTrack::set( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, audio_output_flags_t flags, callback_t cbf, void* user, uint32_t notificationFrames, const sp<IMemory>& sharedBuffer, bool threadCanCallJava, int sessionId, transfer_type transferType, const audio_offload_info_t *offloadInfo, int uid, pid_t pid, const audio_attributes_t* pAttributes, bool doNotReconnect) { //放開這個log可以看出創建AudioTrack時設置的參數,從而判斷后面選擇的output是否正確 ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, " "flags #%x, notificationFrames %u, sessionId %d, transferType %d, uid %d, pid %d", streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames, sessionId, transferType, uid, pid); ...... ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %zu", sharedBuffer->pointer(), sharedBuffer->size()); ALOGV("set() streamType %d frameCount %zu flags %04x", streamType, frameCount, flags); // invariant that mAudioTrack != 0 is true only after set() returns successfully if (mAudioTrack != 0) { ALOGE("Track already in use"); return INVALID_OPERATION; } //當AudioTrack沒有指明streamType時,程序會設為默認值AUDIO_STREAM_MUSIC // handle default values first. if (streamType == AUDIO_STREAM_DEFAULT) { streamType = AUDIO_STREAM_MUSIC; } if (pAttributes == NULL) { if (uint32_t(streamType) >= AUDIO_STREAM_PUBLIC_CNT) { ALOGE("Invalid stream type %d", streamType); return BAD_VALUE; } mStreamType = streamType; } else { // stream type shouldn't be looked at, this track has audio attributes memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t)); ALOGV("Building AudioTrack with attributes: usage=%d content=%d flags=0x%x tags=[%s]", mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags); mStreamType = AUDIO_STREAM_DEFAULT; if ((mAttributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); } } //采樣深度默認為16bit // these below should probably come from the audioFlinger too... if (format == AUDIO_FORMAT_DEFAULT) { format = AUDIO_FORMAT_PCM_16_BIT; } // validate parameters if (!audio_is_valid_format(format)) { ALOGE("Invalid format %#x", format); return BAD_VALUE; } mFormat = format; if (!audio_is_output_channel(channelMask)) { ALOGE("Invalid channel mask %#x", channelMask); return BAD_VALUE; } mChannelMask = channelMask; uint32_t channelCount = audio_channel_count_from_out_mask(channelMask); mChannelCount = channelCount; //flag等參數會影響到選擇哪一個output // force direct flag if format is not linear PCM // or offload was requested if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) || !audio_is_linear_pcm(format)) { ALOGV( (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) ? "Offload request, forcing to Direct Output" : "Not linear PCM, forcing to Direct Output"); flags = (audio_output_flags_t) // FIXME why can't we allow direct AND fast? ((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST); } // force direct flag if HW A/V sync requested if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) { flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT); } if (flags & AUDIO_OUTPUT_FLAG_DIRECT) { if (audio_is_linear_pcm(format)) { mFrameSize = channelCount * audio_bytes_per_sample(format); } else { mFrameSize = sizeof(uint8_t); } } else { ALOG_ASSERT(audio_is_linear_pcm(format)); mFrameSize = channelCount * audio_bytes_per_sample(format); // createTrack will return an error if PCM format is not supported by server, // so no need to check for specific PCM formats here } // sampling rate must be specified for direct outputs if (sampleRate == 0 && (flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) { return BAD_VALUE; } mSampleRate = sampleRate; mOriginalSampleRate = sampleRate; mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; // Make copy of input parameter offloadInfo so that in the future: // (a) createTrack_l doesn't need it as an input parameter // (b) we can support re-creation of offloaded tracks if (offloadInfo != NULL) { mOffloadInfoCopy = *offloadInfo; mOffloadInfo = &mOffloadInfoCopy; } else { mOffloadInfo = NULL; } //左右聲道初始音量都設置成最大 mVolume[AUDIO_INTERLEAVE_LEFT] = 1.0f; mVolume[AUDIO_INTERLEAVE_RIGHT] = 1.0f; mSendLevel = 0.0f; ...... //創 建 track 線 程 if (cbf != NULL) { //AudioTrackThread需要實現兩個核心功能: //1.AudioTrack與AudioFlinger間的數據傳輸,AudioFlinger啟動了一個線程專門用於接收客戶端的 //音頻數據,同理,客戶端也需要一個工作線程來“不斷”的傳送音頻數據 //2.用於報告數據的傳輸狀態,AudioTrack中保存了一個callback_t類型的回調函數(即全局變量mCbf) //用於事件發生時進行回傳 mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); //其實就是調用AudioTrackThread::threadLoop()函數 mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/); // thread begins in paused state, and will not reference us until start() } //AudioTrack在AudioFlinger內部是以Track類來管理的,不過因為他們之間是跨進程的關系,自然需要一個 //橋梁來維護(Binder),這個溝通的媒介是IaudioTrack,createTrack_l除了為AudioTrack在AudioFlinger //中申請一個Track外,還會建立兩者之間的IAudioTrack橋梁 // create the IAudioTrack status_t status = createTrack_l(); if (status != NO_ERROR) { if (mAudioTrackThread != 0) { mAudioTrackThread->requestExit(); // see comment in AudioTrack.h mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } return status; } ...... return NO_ERROR; }
Native層AudioTrack的set函數在對各個參數進行處理之后,調用了creatTrack_l()函數來創建IAudioTrack,與AudioFlinger建立聯系。
//frameworks/av/media/libmedia/AudioTrack.cpp // must be called with mLock held status_t AudioTrack::createTrack_l() { //獲取AudioFlinger的Binder代理 const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); if (audioFlinger == 0) { ALOGE("Could not get audioflinger"); return NO_INIT; } if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) { AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput); } //audio_io_handle_t是一個通過typedef定義在audio.h中的int類型,這個值主要被AudioFlinger使用 //用來表示內部的工作線程的索引號,AudioFlinger會根據情況創建幾個工作線程 //AudioSystem::getOutputForAttr會根據流類型等參數選取一個合適的工作線程 //並將它在AF中的索引號保存在output變量中,AudioTrack一般使用混音線程(MixerThread) audio_io_handle_t output; //AUDIO_STREAM_MUSIC audio_stream_type_t streamType = mStreamType; audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL; status_t status; //根據根據流類型、flag等參數選取一個合適的工作線程(PlaybackThread) status = AudioSystem::getOutputForAttr(attr, &output, (audio_session_t)mSessionId, &streamType, mClientUid, mSampleRate, mFormat, mChannelMask, mFlags, mSelectedDeviceId, mOffloadInfo); if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) { ALOGE("Could not get audio output for session %d, stream type %d, usage %d, sample rate %u, format %#x," " channel mask %#x, flags %#x", mSessionId, streamType, mAttributes.usage, mSampleRate, mFormat, mChannelMask, mFlags); return BAD_VALUE; } ...... size_t temp = frameCount; // temp may be replaced by a revised value of frameCount, // but we will still need the original value also int originalSessionId = mSessionId; //調用createTrack返回AudioFlinger內部的AudioTrack的binder代理 //IAudioTrack是聯系AudioTrack和AudioFlinger的關鍵紐帶 sp<IAudioTrack> track = audioFlinger->createTrack(streamType, mSampleRate, mFormat, mChannelMask, &temp, &trackFlags, mSharedBuffer, output, tid, &mSessionId, mClientUid, &status); ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId, "session ID changed from %d to %d", originalSessionId, mSessionId); if (status != NO_ERROR) { ALOGE("AudioFlinger could not create track, status: %d", status); goto release; } ALOG_ASSERT(track != 0); // AudioFlinger now owns the reference to the I/O handle, // so we are no longer responsible for releasing it. //獲取 track 的共享 buffer //當PlaybackThread創建一個PlaybackThread::Track對象時,所需的緩沖區空間 //就已經分配了,這塊空間是可以跨進程共享的,所以AudioTrack可以通過track->getCblk //來獲取,到目前為止,AudioTrack已經可以通過IaudioTrack(track變量)來調用 //AudioFlinger提供的服務了 //創建了AudioTrack實例之后,應用實例就可以通過不斷寫入(AudioTrack::write) //音頻數據來回放聲音 sp<IMemory> iMem = track->getCblk();//向AudioFlinger申請數據緩沖空間 if (iMem == 0) { ALOGE("Could not get control block"); return NO_INIT; } void *iMemPointer = iMem->pointer(); if (iMemPointer == NULL) { ALOGE("Could not get control block pointer"); return NO_INIT; } // invariant that mAudioTrack != 0 is true only after set() returns successfully if (mAudioTrack != 0) { IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this); mDeathNotifier.clear(); } //與AudioFlinger進行通信的中介 mAudioTrack = track; mCblkMemory = iMem; IPCThreadState::self()->flushCommands(); audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer); mCblk = cblk; // note that temp is the (possibly revised) value of frameCount if (temp < frameCount || (frameCount == 0 && temp == 0)) { // In current design, AudioTrack client checks and ensures frame count validity before // passing it to AudioFlinger so AudioFlinger should not return a different value except // for fast track as it uses a special method of assigning frame count. ALOGW("Requested frameCount %zu but received frameCount %zu", frameCount, temp); } frameCount = temp; ...... // Starting address of buffers in shared memory. If there is a shared buffer, buffers // is the value of pointer() for the shared buffer, otherwise buffers points // immediately after the control block. This address is for the mapping within client // address space. AudioFlinger::TrackBase::mBuffer is for the server address space. void* buffers; if (mSharedBuffer == 0) { buffers = cblk + 1; } else { buffers = mSharedBuffer->pointer(); if (buffers == NULL) { ALOGE("Could not get buffer pointer"); return NO_INIT; } } ...... // reset server position to 0 as we have new cblk. mServer = 0; // update proxy if (mSharedBuffer == 0) { mStaticProxy.clear(); //創建 Client Proxy //在 AudioFlinger 調用 mAudioTrackServerProxy = new AudioTrackServerProxy(),創建 Server Proxy。 // Proxy類封裝了 track 共享 buffer 的操控接口,實現共享 buffer 的使用 mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize); } else { mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize); mProxy = mStaticProxy; } //調用 Proxy 的 set 接口,設置保存 VolumeLR,SampleRate,SendLevel 等參數, //AudioFlinger mixer 線程中會把這些參數取出來實現混音 mProxy->setVolumeLR(gain_minifloat_pack( gain_from_float(mVolume[AUDIO_INTERLEAVE_LEFT]), gain_from_float(mVolume[AUDIO_INTERLEAVE_RIGHT]))); mProxy->setSendLevel(mSendLevel); const uint32_t effectiveSampleRate = adjustSampleRate(mSampleRate, mPlaybackRate.mPitch); const float effectiveSpeed = adjustSpeed(mPlaybackRate.mSpeed, mPlaybackRate.mPitch); const float effectivePitch = adjustPitch(mPlaybackRate.mPitch); mProxy->setSampleRate(effectiveSampleRate); AudioPlaybackRate playbackRateTemp = mPlaybackRate; playbackRateTemp.mSpeed = effectiveSpeed; playbackRateTemp.mPitch = effectivePitch; mProxy->setPlaybackRate(playbackRateTemp); mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this); if (mDeviceCallback != 0) { AudioSystem::addAudioDeviceCallback(mDeviceCallback, mOutput); } return NO_ERROR; } release: AudioSystem::releaseOutput(output, streamType, (audio_session_t)mSessionId); if (status == NO_ERROR) { status = NO_INIT; } return status; }
AudioSystem::getOutputForAttr 最終會調用到AudioPolicyManager::getOutputForAttr它會根據創建AudioTrack時傳入的參數創建一個合適的PlaybackThread,並且選擇合適的輸出設備。我們來看一下AudioPolicyManager::getOutputForAttr這個函數
status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, audio_output_flags_t flags, audio_port_handle_t selectedDeviceId, const audio_offload_info_t *offloadInfo) { ...... //放開這個log可以看出參數、選擇的設備是否正確 ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x" " session %d selectedDeviceId %d", attributes.usage, attributes.content_type, attributes.tags, attributes.flags, session, selectedDeviceId); *stream = streamTypefromAttributesInt(&attributes); ...... mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid); //通過屬性獲取策略 routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); //通過策略獲取輸出設備 audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); } ALOGV("getOutputForAttr() device 0x%x, samplingRate %d, format %x, channelMask %x, flags %x", device, samplingRate, format, channelMask, flags); //通過輸出設備選擇output線程 *output = getOutputForDevice(device, session, *stream, samplingRate, format, channelMask, flags, offloadInfo); if (*output == AUDIO_IO_HANDLE_NONE) { mOutputRoutes.removeRoute(session); return INVALID_OPERATION; } return NO_ERROR; }
它首先根據各個參數來獲取音頻策略,然后根據音頻策略選擇輸出設備,最后通過選擇的設備選擇一個合適的輸出線程。前面的兩個步驟在AudioPolicy中已經分析過了,所以我們重點來看一下getOutputForDevice這個函數
audio_io_handle_t AudioPolicyManager::getOutputForDevice( audio_devices_t device, audio_session_t session __unused, audio_stream_type_t stream, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, audio_output_flags_t flags, const audio_offload_info_t *offloadInfo) { audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; uint32_t latency = 0; status_t status; ...... //最終會調用AudioFlinger::openOutput,返回一個輸出線程保存在output變量中 status = mpClientInterface->openOutput(profile->getModuleHandle(), &output, &config, &outputDesc->mDevice, String8(""), &outputDesc->mLatency, outputDesc->mFlags); ...... ALOGV(" getOutputForDevice() returns output %d", output); //返回前面獲得的output return output; }
緊接着跟蹤進去看一下AudioFlinger::openOutput這個函數
status_t AudioFlinger::openOutput(audio_module_handle_t module, audio_io_handle_t *output, audio_config_t *config, audio_devices_t *devices, const String8& address, uint32_t *latencyMs, audio_output_flags_t flags) { ALOGI("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x", module, (devices != NULL) ? *devices : 0, config->sample_rate, config->format, config->channel_mask, flags); if (*devices == AUDIO_DEVICE_NONE) { return BAD_VALUE; } Mutex::Autolock _l(mLock); sp<PlaybackThread> thread = openOutput_l(module, output, config, *devices, address, flags); if (thread != 0) { *latencyMs = thread->latency(); // notify client processes of the new output creation thread->ioConfigChanged(AUDIO_OUTPUT_OPENED); // the first primary output opened designates the primary hw device if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) { ALOGI("Using module %d has the primary audio interface", module); mPrimaryHardwareDev = thread->getOutput()->audioHwDev; AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_SET_MODE; mPrimaryHardwareDev->hwDevice()->set_mode(mPrimaryHardwareDev->hwDevice(), mMode); mHardwareStatus = AUDIO_HW_IDLE; } return NO_ERROR; } return NO_INIT; }
它具體的創建PlaybackThread是在openOutput_l函數中完成的
//輸入參數中的module是由前面loadNodule來獲得的,它是一個audio interface的id號 //可以通過此id在mAudioHwSevs中查找對應的AudioHwDevice對象 //這個方法中會將打開的output加到mPlaybackThreads線程中 sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(audio_module_handle_t module, audio_io_handle_t *output, audio_config_t *config, audio_devices_t devices, const String8& address, audio_output_flags_t flags) { //1.查找相應的audio interface AudioHwDevice *outHwDev = findSuitableHwDev_l(module, devices); if (outHwDev == NULL) { return 0; } audio_hw_device_t *hwDevHal = outHwDev->hwDevice(); if (*output == AUDIO_IO_HANDLE_NONE) { *output = nextUniqueId(); } mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; // FOR TESTING ONLY: // This if statement allows overriding the audio policy settings // and forcing a specific format or channel mask to the HAL/Sink device for testing. if (!(flags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT))) { // Check only for Normal Mixing mode if (kEnableExtendedPrecision) { // Specify format (uncomment one below to choose) //config->format = AUDIO_FORMAT_PCM_FLOAT; //config->format = AUDIO_FORMAT_PCM_24_BIT_PACKED; //config->format = AUDIO_FORMAT_PCM_32_BIT; //config->format = AUDIO_FORMAT_PCM_8_24_BIT; // ALOGV("openOutput_l() upgrading format to %#08x", config->format); } if (kEnableExtendedChannels) { // Specify channel mask (uncomment one below to choose) //config->channel_mask = audio_channel_out_mask_from_count(4); // for USB 4ch //config->channel_mask = audio_channel_mask_from_representation_and_bits( // AUDIO_CHANNEL_REPRESENTATION_INDEX, (1 << 4) - 1); // another 4ch example } } AudioStreamOut *outputStream = NULL; //2.為設備打開一個輸出流,創建Audio HAL的音頻輸出對象 status_t status = outHwDev->openOutputStream( &outputStream, *output, devices, flags, config, address.string()); mHardwareStatus = AUDIO_HW_IDLE; //3. 根據標志不同創建不同的playbackThread if (status == NO_ERROR) { PlaybackThread *thread; if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady); ALOGV("openOutput_l() created offload output: ID %d thread %p", *output, thread); } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) || !isValidPcmSinkFormat(config->format) || !isValidPcmSinkChannelMask(config->channel_mask)) { thread = new DirectOutputThread(this, outputStream, *output, devices, mSystemReady); ALOGV("openOutput_l() created direct output: ID %d thread %p", *output, thread); } else { //一般是創建混音線程,代表AudioStreamOut對象的output也傳遞進去了 thread = new MixerThread(this, outputStream, *output, devices, mSystemReady); ALOGV("openOutput_l() created mixer output: ID %d thread %p", *output, thread); } mPlaybackThreads.add(*output, thread);//添加播放線程 return thread; } return 0; }
第一步AudioFlinge會判斷是否有合適的HAL interface(primary、a2dp等)已經被加載了,如果沒有加載就去加載它,如果加載了就直接使用。第二步openOutputStream最終會調用到hal層的open_output_stream,主要是配置輸出PCM設備的一些參數,第三步是去創建PlaybackThread,這里我們具體去分析PlaybackThread是怎么被創建出來的。創建MixerThread的主要流程如下圖所示。
MixerThread在第一次被引用時進入到threadLoop()函數中下面看他做了哪些事
bool AudioFlinger::PlaybackThread::threadLoop() { ...... //准備音頻數據 mMixerStatus = prepareTracks_l(&tracksToRemove); //音頻數據准備就緒,開始混音 ...... hreadLoop_mix(); ...... //將混音好的數據傳遞到Audio HAL ret = threadLoop_write(); ...... }
下面我們進入到prepareTracks_l看一下是怎樣准備音頻數據的
AudioFlinger::MixerThread::prepareTracks_l( Vector< sp<Track> > *tracksToRemove) { ...... //當前活躍的track數量 size_t count = mActiveTracks.size(); ...... //循環處理每一個track for (size_t i=0 ; i<count ; i++) ...... //FastTrack下的處理 if (track->isFastTrack()) ...... //數據塊准備 audio_track_cblk_t* cblk = track->cblk(); ........ //為混音器設置BufferProvider和一些參數 mAudioMixer->setBufferProvider(name, track); mAudioMixer->enable(name); mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf); ...... }
下面我們回到AudioFlinger::PlaybackThread::threadLoop()函數中進入hreadLoop_mix()函數中看一下音頻數據准備好之后AudioMixer是怎樣進行混音的
void AudioFlinger::MixerThread::threadLoop_mix() { ...... // mix buffers... mAudioMixer->process(pts); ...... }
進入到AudioMixe::process()函數中
void AudioMixer::process(int64_t pts) { mState.hook(&mState, pts); }
這里面只有一行代碼,就是調用了mState.hook,mState是一個state_t結構體類型的變量,state_t結構定義在AudioMixer.h中
typedef void (*process_hook_t)(state_t* state, int64_t pts); // pad to 32-bytes to fill cache line struct state_t { uint32_t enabledTracks; uint32_t needsChanged; size_t frameCount; process_hook_t hook; // one of process__*, never NULL int32_t *outputTemp; int32_t *resampleTemp; NBLog::Writer* mLog; int32_t reserved[1]; track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32))); };
可以看出hook是一個函數指針,我們就是通過這個函數指針指向的函數來處理音頻數據的,它有好幾種實現
process__calidate:根據當前的具體情況,進一步將hook導向下面幾個函數
process__OneTrack16BitsStereoNoResampling:只有一路Track,16比特立體聲,不重采樣
process__GenericNoResampling:兩路(包含)以上Track不重采樣
process__GenericResampling:兩路(包含)以上Track重采樣
......
AudioMixer::process是外部程序調用hook的入口
下面我們以process_NoResampleOneTrack為例進行分析
void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts) { ....... t->bufferProvider->getNextBuffer(&b, outputPTS); ...... t->bufferProvider->releaseBuffer(&b); ...... }
上面的的BufferProvider是上面MixerThread::prepareTracks_l函數中我們為AudioMixter設置參數傳進來的
mAudioMixer->setBufferProvider(name, track);
下面我們來分析一下BufferProvider::getNextBuffer函數的實現
status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, int64_t pts) { ...... status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts); ...... }
它調用了Track::getNextBuffer繼續來分析
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer( AudioBufferProvider::Buffer* buffer, int64_t pts __unused) { ServerProxy::Buffer buf; size_t desiredFrames = buffer->frameCount; buf.mFrameCount = desiredFrames; status_t status = mServerProxy->obtainBuffer(&buf); buffer->frameCount = buf.mFrameCount; buffer->raw = buf.mRaw; if (buf.mFrameCount == 0) { mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames); } return status; }
它調用了ServerProxy::obtainBuffer,ServerProxy::obtainBuffer和調用AudioTrack::write的時候調用到的ClientProxy::obtainBuffer是相似的。ServerProxy::releaseBuffer和ClientProxy::releaseBuffer也是類似的,AudioTrack和AudioFlinger通過mCblkMemory這塊內存來實現“生產者-消費者”數據交互,下面我們來分析一下ServerProxy和ClientProxy通過共享內存進行數據交互的原理。
創建 track 的時候,AudioFlinger 會給每個 track 分配 audio 共享 buffer,AudioTrack、AudioFlinger 以該buffer 為參數通過 AudioTrackClientProxy、AudioTrackServerProxy 創建 mClientProxy、mServerProxy。AudioTrack( client 端)通過 mClientProxy 向共享 buffer 寫入數據, AudioFlinger(server 端)通過 mServerProxy 從共享 buffer 讀出數據。這樣 client、server 通過 proxy 對共享 buffer 形成了生產者、消費者模型。AudioTrackClientProxy、AudioTrackServerProxy( 這兩個類都位於 AudioTrackShared.cpp )分別封裝了 client 端、server 端共享 buffer 的使用方法 obtainBuffer 和 releaseBuffer,這些接口的功能如下:
Client 端:
AudioTrackClientProxy:: obtainBuffer()從 audio buffer 獲取連續的 empty buffer;
AudioTrackClientProxy:: releaseBuffer ()將填充了數據的 buffer 放回 audio buffer。
Server 端:
AudioTrackServerProxy:: obtainBuffer()從 audio buffer 獲取連續的填充了數據的 buffer;
AudioTrackServerProxy:: releaseBuffer ()將使用完的 empty buffer 放回 audio buffer。
2.2.開始播放
上面應用層開始播放PCM數據是通過調用AudioTrack的play方法來實現的,它的主要流程如下圖所示。
它經過層層的調用,最終調用到了AudioPolicyManager的startOutput函數,這個在學習AudioPolicy的時候分析過了,所以先跳過。
2.3.往AudioTrack中寫數據
在調用了AudioTrack的play函數開始播放PCM音頻數據之后,我們會調用AudioTrack的write函數來向AudioTrack中寫數據。它的主要流程如下圖所示。
我們直接分析native層AudioTrack的write函數。
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking) { if (mTransfer != TRANSFER_SYNC || mIsTimed) { return INVALID_OPERATION; }//STATIC模式下,數據是一次就寫完的,不需要調用write ...... size_t written = 0; uffer audioBuffer; //寫入數據 while (userSize >= mFrameSize) {//傳過來的數據大小大於1幀數據 audioBuffer.frameCount = userSize / mFrameSize; status_t err = obtainBuffer(&audioBuffer, blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking); //obtainBuffer出錯 if (err < 0) { if (written > 0) { break; } return ssize_t(err); } size_t toWrite = audioBuffer.size; //內存復制 memcpy(audioBuffer.i8, buffer, toWrite); //指針跳過已寫入的數據 buffer = ((const char *) buffer) + toWrite; //剩余的數據量 userSize -= toWrite; //已經寫入的數據量 written += toWrite; //釋放audioBuffer releaseBuffer(&audioBuffer); } return written }
首先我們需要使用obtainBuffer來獲取目標區的寫入地址下面我們來分析一下它具體是怎樣實現的:
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, struct timespec *elapsed, size_t *nonContig) { ...... sp<AudioTrackClientProxy> proxy; sp<IMemory> iMem; ...... // Keep the extra references proxy = mProxy; iMem = mCblkMemory; status = proxy->obtainBuffer(&buffer, requested, elapsed); }
從上面代碼可以發現其調用了AudioTrackClientProxy::obtainBuffer所以我們進入到AudioTrackClientProxy::obtainBuffer分析其獲取buffer的步驟
// frameworks/av/media/libmedia/AudioTrackShared.cpp*/ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested, struct timespec *elapsed) { ...... //step1:獲取 audio 共享 buffer 的讀寫指針 rear,front //由於讀指針 front 在 AudioFlinger 端也會被操作 //這里調用 android_atomic_acquire_load 接口獲取,該接口采用了內存屏障技術, //實現共享變量的讀寫同步訪問; front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); rear = cblk->u.mStreaming.mRear; ...... //step2:計算出當前 buffer 中可用的 empty buffer 數量; size_t avail = mIsOut ? mFrameCount - filled : filled; ...... //step3:rear &= mFrameCountP2 – 1,計算出寫指針 rear 在 buffer 中的真實偏移量; //part1 = mFrameCountP2 – rear,計算出從寫指針開始 buffer 最大的連續地址長度 rear &= mFrameCountP2 - 1; part1 = mFrameCountP2 - rear; ...... //step4最大連續地址長度大於可用 empty buffer 值時,將其值修正為 empty buffer 值,這種情況說明所有可用 //的 empty buffer 都是地址連續的;然后再將 part1 與要求的 buffer 數量比較,大於要 求數量,則修正其 //值為要求的 buffer 數量,這種情況說明在本次操作中能完全滿足要求,一次性完成 if (part1 > avail) { part1 = avail; } //step5填 充 buffer->mFrameCount ( 實 際 獲 取 的 buffer 數 量 ), buffer->mRaw ( buffer 的 起 始 指 針 ), //buffer->mNonContig(未使用的不連續 empty buffer 數量),mUnreleased(需要釋放的 buffer 數量,在releaseBuffer 中釋放) buffer->mFrameCount = part1; buffer->mRaw = part1 > 0 ? &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL; buffer->mNonContig = avail - part1; mUnreleased = part1; ...... }
在申請完buffer並填充完音頻數據之后我們需要釋放buffer以供AudioFlinger獲取並提取其中的音頻數據信息,這個步驟是通過調用releaseBuffer來實現的,下面我們就來分析一下具體的實現方式。
void AudioTrack::releaseBuffer(const Buffer* audioBuffer) { ...... Proxy::Buffer buffer; buffer.mFrameCount = stepCount; buffer.mRaw = audioBuffer->raw; mProxy->releaseBuffer(&buffer); }
可見,和obtainBuffer一樣也是通過調用AudioTrackClientProxy::releaseBuffer來實現的,下面就來分析AudioTrackClientProxy::releaseBuffer
proxy->obtainBuffervoid ClientProxy::releaseBuffer(Buffer* buffer) { ...... size_t stepCount = buffer->mFrameCount; ...... mUnreleased -= stepCount; audio_track_cblk_t* cblk = mCblk; if (mIsOut) { int32_t rear = cblk->u.mStreaming.mRear; android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear); } }
該接口很簡單,stepCount = buffer->mFrameCount,獲取需要釋放的 buffer 數量,然后獲取寫指針 rear,調用android_atomic_release_store 更新 rear
AudioTrack和AudioFlinger通過mCblkMemory這塊內存來實現“生產者-消費者”數據交互,AudioTrack將音頻數據寫入到共享內存中,下AudioFlinger將音頻數據從共享內存中讀取出來的。