寫(xiě)在之前
本來(lái)有打算寫(xiě)寫(xiě)Android音頻系統(tǒng)的,但是仔細(xì)研究了如下鏈接的三篇文章,果斷中斷了我的想法。毫不夸張來(lái)說(shuō),這是我看過(guò)的最好的闡述Android音頻系統(tǒng)的文章了,簡(jiǎn)練精辟,將音頻系統(tǒng)各個(gè)方面的重要的脈絡(luò)都描述出來(lái)了。有這三篇文章,理解Android音頻系統(tǒng)何止加快了10倍。
Android Audio System 之一:AudioTrack如何與AudioFlinger交換音頻數(shù)據(jù)
Android Audio System 之二:AudioFlinger
Android Audio System 之三: AudioPolicyService 和 AudioPolicyManager
A2dpAudioInterface
Android音頻系統(tǒng)有兩大服務(wù):一是AudioFlinger,二是AudioPolicyService。AudioFlinger負(fù)責(zé)向下訪問(wèn)AudioHardwareInterface,實(shí)現(xiàn)音頻PCM數(shù)據(jù)的混音/輸入/輸出,實(shí)現(xiàn)音量調(diào)節(jié);AudioPolicyService負(fù)責(zé)音頻輸入輸出設(shè)備的連接狀態(tài),音頻策略調(diào)度即音頻設(shè)備(如本地CODEC、Bluetooth A2DP、Headset)的切換策略(注意它只是負(fù)責(zé)策略,真正的切換操作是在AudioFlinger中的openOutput,畢竟AudioFlinger負(fù)責(zé)操作底層音頻硬件)。AudioPolicyService在以后的章節(jié)詳細(xì)分析,這里主要探討A2DP-Audio是如何注冊(cè)到AudioFlinger中,并簡(jiǎn)要提及音頻PCM數(shù)據(jù)流向。
好的平臺(tái)軟件應(yīng)有這樣的一個(gè)抽象層:向下提供一套固定的接口,不同的硬件設(shè)備根據(jù)這些接口實(shí)現(xiàn)各自的方法,然后注冊(cè)到這個(gè)抽象層中去。這樣對(duì)于上層應(yīng)用而言并沒(méi)有任何區(qū)別,因?yàn)樯蠈又恍枵{(diào)用抽象層接口就行了,不管底層硬件的差異性。AudioFlinger就是這樣的一個(gè)抽象層,無(wú)論底層是ALSA設(shè)備還是BluetoothHeadset,上層都只會(huì)看到AudioFlinger的接口。至于何時(shí)切換到ALSA設(shè)備何時(shí)切換到BluetoothHeadset,這就屬于音頻策略調(diào)度范疇了即AudioPolicyService。
- AudioFlinger::AudioFlinger()
- : BnAudioFlinger(),
- mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
- {
- mHardwareStatus = AUDIO_HW_IDLE;
-
- mAudioHardware = AudioHardwareInterface::create();
- ......
再看AudioHardwareInterface::create():
- AudioHardwareInterface* AudioHardwareInterface::create()
- {
-
-
-
-
- AudioHardwareInterface* hw = 0;
- char value[PROPERTY_VALUE_MAX];
-
- #ifdef GENERIC_AUDIO
- hw = new AudioHardwareGeneric();
- #else
-
- if (property_get("ro.kernel.qemu", value, 0)) {
- LOGD("Running in emulation - using generic audio driver");
- hw = new AudioHardwareGeneric();
- }
- else {
- LOGV("Creating Vendor Specific AudioHardware");
- hw = createAudioHardware();
- }
- #endif
- if (hw->initCheck() != NO_ERROR) {
- LOGW("Using stubbed audio hardware. No sound will be produced.");
- delete hw;
- hw = new AudioHardwareStub();
- }
-
- #ifdef WITH_A2DP
- hw = new A2dpAudioInterface(hw);
- #endif
-
- #ifdef ENABLE_AUDIO_DUMP
-
-
-
-
-
- LOGV("opening PCM dump interface");
- hw = new AudioDumpInterface(hw);
- #endif
- return hw;
- }
這個(gè)函數(shù)我在
ANDROID2.3音頻系統(tǒng)HAL有簡(jiǎn)要的分析,現(xiàn)在我們接著往下看看A2DP的注冊(cè):
hw = new A2dpAudioInterface(hw);
注意紅色部分hw,為什么A2dpAudioInterface還需要createAudioHardware()打開(kāi)的AudioHardwareInterface(我們假設(shè)這是ALSA設(shè)備接口)呢?如我們所知,BluetoothA2DP與ALSA設(shè)備并不走同一套接口,因此Android的設(shè)計(jì)者就把ALSA設(shè)備接口扔到A2DP接口里面管理了。這又是如何管理呢?簡(jiǎn)單來(lái)說(shuō),就是根據(jù)上層傳下來(lái)的參數(shù)devices,判斷devices是否是DEVICE_OUT_BLUETOOTH_A2DP,如果是則走A2DP接口,如果不是則走ALSA設(shè)備接口。例如需要打開(kāi)一個(gè)音頻輸出流時(shí):
- AudioStreamOut* A2dpAudioInterface::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
- {
- if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
- LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
- return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
- }
-
- status_t err = 0;
-
-
- if (mOutput) {
- if (status)
- *status = -1;
- return NULL;
- }
-
-
- A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
- if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
- mOutput = out;
- mOutput->setBluetoothEnabled(mBluetoothEnabled);
- mOutput->setSuspended(mSuspended);
- } else {
- delete out;
- }
-
- if (status)
- *status = err;
- return mOutput;
- }
當(dāng)上層傳下來(lái)的devices不屬于A2DP設(shè)備時(shí),則return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);其中mHardwareInterface保存的是ALSA的hw。否則A2dpAudioStreamOut* out = new A2dpAudioStreamOut();為A2DP打開(kāi)一個(gè)音頻輸出流。
liba2dp
到了A2dpAudioInterface這層,就是訪問(wèn)BlueZ的音頻操作接口了,主要是external\bluetooth\bluez\audio\liba2dp.c。liba2dp.c代碼或許很復(fù)雜,我也沒(méi)有深入了解過(guò),但是接口卻非常簡(jiǎn)單易用。看liba2dp.h,僅僅只有幾個(gè)接口:
- int a2dp_init(int rate, int channels, a2dpData* dataPtr);
- void a2dp_set_sink(a2dpData data, const char* address);
- int a2dp_write(a2dpData data, const void* buffer, int count);
- int a2dp_stop(a2dpData data);
- void a2dp_cleanup(a2dpData data);
a2dp_init:根據(jù)傳入來(lái)的采樣率rate,聲道數(shù)channels初始化一個(gè)a2dpData;
a2dp_set_sink:綁定一個(gè)藍(lán)牙地址address到a2dpData上;
a2dp_write:往a2dp寫(xiě)入音頻PCM數(shù)據(jù);
a2dp_stop:停止a2dp播放。
例如,每當(dāng)有音頻PCM數(shù)據(jù)需要送入Bluetooth時(shí):
- ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
- {
- Mutex::Autolock lock(mLock);
-
- size_t remaining = bytes;
- status_t status = -1;
-
- if (!mBluetoothEnabled || mClosing || mSuspended) {
- LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
- mBluetoothEnabled %d, mClosing %d, mSuspended %d",
- mBluetoothEnabled, mClosing, mSuspended);
- goto Error;
- }
-
- status = init();
- if (status < 0)
- goto Error;
-
- while (remaining > 0) {
- status = a2dp_write(mData, buffer, remaining);
- if (status <= 0) {
- LOGE("a2dp_write failed err: %d\n", status);
- goto Error;
- }
- remaining -= status;
- buffer = ((char *)buffer) + status;
- }
-
- mStandby = false;
-
- return bytes;
-
- Error:
-
- usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);
-
- return status;
- }
核心語(yǔ)句:status = a2dp_write(mData, buffer, remaining); 只需要傳入音頻數(shù)據(jù)的首地址和大小就行了。
該函數(shù)在AudioFlinger::MixerThread::threadLoop()調(diào)用,下面簡(jiǎn)要介紹音頻數(shù)據(jù)從上層到底層硬件設(shè)備的傳輸流向過(guò)程。