android 4.0 电话录音,ANDROID音频系统散记之四:4.0音频系统HAL初探
昨天(2011-11-15)發布了Android4.0的源碼,今天download下來,開始挺進4.0時代。簡單看了一下,發現音頻系統方面與2.3的有較多地方不同,下面逐一描述。
一、代碼模塊位置
1、AudioFlinger
frameworks/base/services/audioflinger/
+--?Android.mk
+--?AudioBufferProvider.h
+--?AudioFlinger.cpp
+--?AudioFlinger.h
+--?AudioMixer.cpp
+--?AudioMixer.h
+--?AudioPolicyService.cpp
+--?AudioPolicyService.h
+--?AudioResampler.cpp
+--?AudioResamplerCubic.cpp
+--?AudioResamplerCubic.h
+--?AudioResampler.h
+--?AudioResamplerSinc.cpp
+--?AudioResamplerSinc.hAudioFlinger相關代碼,好像這部分與2.3相差不大,至少接口是兼容的。值得注意的是:2.3位于這里的還有AudioHardwareGeneric、AudioHardwareInterface、A2dpAudioInterface等一系列接口代碼,現在都移除了。實際上,這些接口變更為legacy(有另外更好的實現方式,但也兼容之前的方法),取而代之的是要實現hardware/libhardware/include/hardware/audio.h提供的接口,這是一個較大的變化。
兩種Audio Hardware HAL接口定義:
1/ legacy:hardware/libhardware_legacy/include/hardware_legacy/AudioHardwareInterface.h
2/ 非legacy:hardware/libhardware/include/hardware/audio.h
2、audio_hw
hardware/libhardware_legacy/audio/
+--?A2dpAudioInterface.cpp
+--?A2dpAudioInterface.h
+--?Android.mk
+--?AudioDumpInterface.cpp
+--?AudioDumpInterface.h
+--?AudioHardwareGeneric.cpp
+--?AudioHardwareGeneric.h
+--?AudioHardwareInterface.cpp
+--?AudioHardwareStub.cpp
+--?AudioHardwareStub.h
+--?audio_hw_hal.cpp
+--?AudioPolicyCompatClient.cpp
+--?AudioPolicyCompatClient.h
+--?audio_policy_hal.cpp
+--?AudioPolicyManagerBase.cpp
+--?AudioPolicyManagerDefault.cpp
+--?AudioPolicyManagerDefault.h上面提及的AudioHardwareGeneric、AudioHardwareInterface、A2dpAudioInterface等都放到libhardware_legacy里。
事實上legacy也要封裝成非legacy中的audio.h,確切的說需要一個聯系legacy interface和not legacy interface的中間層,這里的audio_hw_hal.cpp就充當這樣的一個角色了。因此,我們其實也可以把2.3之前的alsa_sound這一套東西也搬過來。
hardware/libhardware/modules/audio/
+--?Android.mk
+--?audio_hw.c
+--?audio_policy.c這是一個stub(類似于2.3中的AudioHardwareStub),大多數函數只是簡單的返回一個值,并沒有實際操作,只是保證Android能得到一個audio hardware hal實例,從而啟動運行,當然聲音沒有輸出到外設的。在底層音頻驅動或audio hardware hal還沒有實現好的情況下,可以使用這個stub device,先讓Android跑起來。
device/samsung/tuna/audio/
+--?Android.mk
+--?audio_hw.c
+--?ril_interface.c
+--?ril_interface.h這是Samsung Tuna的音頻設備抽象層,很有參考價值,計劃以后就在它的基礎上進行移植。它調用tinyalsa的接口,可見這個方案的底層音頻驅動是alsa。
3、tinyalsa
external/tinyalsa/
+--?Android.mk
+--?include
|???+--?tinyalsa
|???????+--?asoundlib.h
+--?mixer.c??????##類alsa-lib的control,作用音頻部件開關、音量調節等
+--?pcm.c????????##類alsa-lib的pcm,作用音頻pcm數據回放錄制
+--?README
+--?tinycap.c????##類alsa_arecord
+--?tinymix.c????##類alsa_amixer
+--?tinyplay.c???##類alsa_aplay在2.3時代,Android還隱晦把它放在android2.3.1-gingerbread/device/samsung/crespo/libaudio,現在終于把alsa-lib一腳踢開,小三變正室了,正名tinyalsa。
這其實是歷史的必然了,alsa-lib太過復雜繁瑣了,我看得也很不爽;更重要的商業上面的考慮,必須移除被GNU GPL授權證所約束的部份,alsa-lib并不是個例。
注意:上面的hardware/libhardware_legacy/audio/、hardware/libhardware/modules/audio/、device/samsung/tuna/audio/是同層的。之一是legacy audio,用于兼容2.2時代的alsa_sound;之二是stub audio接口;之三是Samsung Tuna的音頻抽象層實現。調用層次:AudioFlinger -> audio_hw -> tinyalsa。
二、Audio Hardware HAL加載
1、AudioFlinger
//加載audio?hardware?hal
staticintload_audio_interface(constchar*if_name,consthw_module_t?**mod,
audio_hw_device_t?**dev)
{
intrc;
//根據classid和if_name找到指定的動態庫并加載,這里加載的是音頻動態庫,如libaudio.primary.tuna.so
rc?=?hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID,?if_name,?mod);
if(rc)
gotoout;
//加載好的動態庫模塊必有個open方法,調用open方法打開音頻設備模塊
rc?=?audio_hw_device_open(*mod,?dev);
LOGE_IF(rc,?"couldn't?open?audio?hw?device?in?%s.%s?(%s)",
AUDIO_HARDWARE_MODULE_ID,?if_name,?strerror(-rc));
if(rc)
gotoout;
return0;
out:
*mod?=?NULL;
*dev?=?NULL;
returnrc;
}
//音頻設備接口,hw_get_module_by_class需要根據這些字符串找到相關的音頻模塊庫
staticconstchar*audio_interfaces[]?=?{
"primary",//主音頻設備,一般為本機codec
"a2dp",//a2dp設備,藍牙高保真音頻
"usb",//usb-audio設備,這個東東我2.3就考慮要實現了,現在終于支持了
};
#define?ARRAY_SIZE(x)?(sizeof((x))/sizeof(((x)[0])))
//?----------------------------------------------------------------------------
AudioFlinger::AudioFlinger()
:?BnAudioFlinger(),
mPrimaryHardwareDev(0),?mMasterVolume(1.0f),?mMasterMute(false),?mNextUniqueId(1),
mBtNrecIsOff(false)
{
}
voidAudioFlinger::onFirstRef()
{
intrc?=?0;
Mutex::Autolock?_l(mLock);
/*?TODO:?move?all?this?work?into?an?Init()?function?*/
mHardwareStatus?=?AUDIO_HW_IDLE;
//打開audio_interfaces數組定義的所有音頻設備
for(size_ti?=?0;?i
consthw_module_t?*mod;
audio_hw_device_t?*dev;
rc?=?load_audio_interface(audio_interfaces[i],?&mod,?&dev);
if(rc)
continue;
LOGI("Loaded?%s?audio?interface?from?%s?(%s)",?audio_interfaces[i],
mod->name,?mod->id);
mAudioHwDevs.push(dev);?//mAudioHwDevs是一個Vector,存儲已打開的audio?hw?devices
if(!mPrimaryHardwareDev)?{
mPrimaryHardwareDev?=?dev;
LOGI("Using?'%s'?(%s.%s)?as?the?primary?audio?interface",
mod->name,?mod->id,?audio_interfaces[i]);
}
}
mHardwareStatus?=?AUDIO_HW_INIT;
if(!mPrimaryHardwareDev?||?mAudioHwDevs.size()?==?0)?{
LOGE("Primary?audio?interface?not?found");
return;
}
//對audio?hw?devices進行一些初始化,如mode、master?volume的設置
for(size_ti?=?0;?i
audio_hw_device_t?*dev?=?mAudioHwDevs[i];
mHardwareStatus?=?AUDIO_HW_INIT;
rc?=?dev->init_check(dev);
if(rc?==?0)?{
AutoMutex?lock(mHardwareLock);
mMode?=?AUDIO_MODE_NORMAL;
mHardwareStatus?=?AUDIO_HW_SET_MODE;
dev->set_mode(dev,?mMode);
mHardwareStatus?=?AUDIO_HW_SET_MASTER_VOLUME;
dev->set_master_volume(dev,?1.0f);
mHardwareStatus?=?AUDIO_HW_IDLE;
}
}
}
以上對AudioFlinger進行的分析,主要是通過hw_get_module_by_class()找到模塊接口名字if_name相匹配的模塊庫,加載,然后audio_hw_device_open()調用模塊的open方法,完成音頻設備模塊的初始化。
留意AudioFlinger的構造函數只有簡單的私有變量的初始化操作了,把音頻設備初始化放到onFirstRef(),Android終于改進了這一點,好的設計根本不應該把可能會失敗的操作放到構造函數中。onFirstRef是RefBase類的一個虛函數,在構造sp的時候就會被調用。因此,在構造sp的時候就會觸發onFirstRef方法,從而完成音頻設備模塊初始化。
2、hw_get_module_by_class
我們接下來看看hw_get_module_by_class,實現在hardware/libhardware/ hardware.c中,它作用加載指定名字的模塊庫(.so文件),這個應該是用于加載所有硬件設備相關的庫文件,并不只是音頻設備。
inthw_get_module_by_class(constchar*class_id,constchar*inst,
conststructhw_module_t?**module)
{
intstatus;
inti;
conststructhw_module_t?*hmi?=?NULL;
charprop[PATH_MAX];
charpath[PATH_MAX];
charname[PATH_MAX];
if(inst)
snprintf(name,?PATH_MAX,?"%s.%s",?class_id,?inst);
else
strlcpy(name,?class_id,?PATH_MAX);
//這里我們以音頻庫為例,AudioFlinger調用到這個函數時,
//class_id=AUDIO_HARDWARE_MODULE_ID="audio",inst="primary"(或"a2dp"或"usb")
//那么此時name="audio.primary"
/*
*?Here?we?rely?on?the?fact?that?calling?dlopen?multiple?times?on
*?the?same?.so?will?simply?increment?a?refcount?(and?not?load
*?a?new?copy?of?the?library).
*?We?also?assume?that?dlopen()?is?thread-safe.
*/
/*?Loop?through?the?configuration?variants?looking?for?a?module?*/
for(i=0?;?i
if(i
//通過property_get找到廠家標記如"ro.product.board=tuna",這時prop="tuna"
if(property_get(variant_keys[i],?prop,?NULL)?==?0)?{
continue;
}
snprintf(path,?sizeof(path),"%s/%s.%s.so",
HAL_LIBRARY_PATH2,?name,?prop);?//#define?HAL_LIBRARY_PATH2?"/vendor/lib/hw"
if(access(path,?R_OK)?==?0)break;
snprintf(path,?sizeof(path),"%s/%s.%s.so",
HAL_LIBRARY_PATH1,?name,?prop);?//#define?HAL_LIBRARY_PATH1?"/system/lib/hw"
if(access(path,?R_OK)?==?0)break;
}?else{
snprintf(path,?sizeof(path),"%s/%s.default.so",//如沒有指定的庫文件,則加載default.so,即stub-device
HAL_LIBRARY_PATH1,?name);
if(access(path,?R_OK)?==?0)break;
}
}
//到這里,完成一個模塊庫的完整路徑名稱,如path="/system/lib/hw/audio.primary.tuna.so"
//如何生成audio.primary.tuna.so?請看相關的Android.mk文件,其中有定義LOCAL_MODULE?:=?audio.primary.tuna
status?=?-ENOENT;
if(i
/*?load?the?module,?if?this?fails,?we're?doomed,?and?we?should?not?try
*?to?load?a?different?variant.?*/
status?=?load(class_id,?path,?module);?//加載模塊庫
}
returnstatus;
}
load()函數不詳細分析了,它通過dlopen加載庫文件,然后dlsym找到hal_module_info的首地址。我們先看看hal_module_info的定義:
/**
*?Every?hardware?module?must?have?a?data?structure?named?HAL_MODULE_INFO_SYM
*?and?the?fields?of?this?data?structure?must?begin?with?hw_module_t
*?followed?by?module?specific?information.
*/
typedefstructhw_module_t?{
/**?tag?must?be?initialized?to?HARDWARE_MODULE_TAG?*/
uint32_t?tag;
/**?major?version?number?for?the?module?*/
uint16_t?version_major;
/**?minor?version?number?of?the?module?*/
uint16_t?version_minor;
/**?Identifier?of?module?*/
constchar*id;
/**?Name?of?this?module?*/
constchar*name;
/**?Author/owner/implementor?of?the?module?*/
constchar*author;
/**?Modules?methods?*/
structhw_module_methods_t*?methods;
/**?module's?dso?*/
void*?dso;
/**?padding?to?128?bytes,?reserved?for?future?use?*/
uint32_t?reserved[32-7];
}?hw_module_t;
typedefstructhw_module_methods_t?{
/**?Open?a?specific?device?*/
int(*open)(conststructhw_module_t*?module,constchar*?id,
structhw_device_t**?device);
}?hw_module_methods_t;這個結構體很重要,注釋很詳細。dlsym拿到這個結構體的首地址后,就可以調用Modules methods進行設備模塊的初始化了。設備模塊中,都應該按照這個格式初始化好這個結構體,否則dlsym找不到它,也就無法調用Modules methods進行初始化了。
例如,在audio_hw.c中,它是這樣定義的:
staticstructhw_module_methods_t?hal_module_methods?=?{
.open?=?adev_open,
};
structaudio_module?HAL_MODULE_INFO_SYM?=?{
.common?=?{
.tag?=?HARDWARE_MODULE_TAG,
.version_major?=?1,
.version_minor?=?0,
.id?=?AUDIO_HARDWARE_MODULE_ID,
.name?=?"Tuna?audio?HW?HAL",
.author?=?"The?Android?Open?Source?Project",
.methods?=?&hal_module_methods,
},
};3、audio_hw
好了,經過一番周折,又dlopen又dlsym的,終于進入我們的audio_hw。這部分沒什么好說的,按照hardware/libhardware/include/hardware/audio.h定義的接口實現就行了。這些接口全扔到一個結構體里面的,這樣做的好處是:不必用大量的dlsym來獲取各個接口函數的地址,只需找到這個結構體即可,從易用性和可擴充性來說,都是首選方式。
接口定義如下:
structaudio_hw_device?{
structhw_device_t?common;
/**
*?used?by?audio?flinger?to?enumerate?what?devices?are?supported?by
*?each?audio_hw_device?implementation.
*
*?Return?value?is?a?bitmask?of?1?or?more?values?of?audio_devices_t
*/
uint32_t?(*get_supported_devices)(conststructaudio_hw_device?*dev);
/**
*?check?to?see?if?the?audio?hardware?interface?has?been?initialized.
*?returns?0?on?success,?-ENODEV?on?failure.
*/
int(*init_check)(conststructaudio_hw_device?*dev);
/**?set?the?audio?volume?of?a?voice?call.?Range?is?between?0.0?and?1.0?*/
int(*set_voice_volume)(structaudio_hw_device?*dev,floatvolume);
/**
*?set?the?audio?volume?for?all?audio?activities?other?than?voice?call.
*?Range?between?0.0?and?1.0.?If?any?value?other?than?0?is?returned,
*?the?software?mixer?will?emulate?this?capability.
*/
int(*set_master_volume)(structaudio_hw_device?*dev,floatvolume);
/**
*?setMode?is?called?when?the?audio?mode?changes.?AUDIO_MODE_NORMAL?mode
*?is?for?standard?audio?playback,?AUDIO_MODE_RINGTONE?when?a?ringtone?is
*?playing,?and?AUDIO_MODE_IN_CALL?when?a?call?is?in?progress.
*/
int(*set_mode)(structaudio_hw_device?*dev,intmode);
/*?mic?mute?*/
int(*set_mic_mute)(structaudio_hw_device?*dev,boolstate);
int(*get_mic_mute)(conststructaudio_hw_device?*dev,bool*state);
/*?set/get?global?audio?parameters?*/
int(*set_parameters)(structaudio_hw_device?*dev,constchar*kv_pairs);
/*
*?Returns?a?pointer?to?a?heap?allocated?string.?The?caller?is?responsible
*?for?freeing?the?memory?for?it.
*/
char*?(*get_parameters)(conststructaudio_hw_device?*dev,
constchar*keys);
/*?Returns?audio?input?buffer?size?according?to?parameters?passed?or
*?0?if?one?of?the?parameters?is?not?supported
*/
size_t(*get_input_buffer_size)(conststructaudio_hw_device?*dev,
uint32_t?sample_rate,?intformat,
intchannel_count);
/**?This?method?creates?and?opens?the?audio?hardware?output?stream?*/
int(*open_output_stream)(structaudio_hw_device?*dev,?uint32_t?devices,
int*format,?uint32_t?*channels,
uint32_t?*sample_rate,
structaudio_stream_out?**out);
void(*close_output_stream)(structaudio_hw_device?*dev,
structaudio_stream_out*?out);
/**?This?method?creates?and?opens?the?audio?hardware?input?stream?*/
int(*open_input_stream)(structaudio_hw_device?*dev,?uint32_t?devices,
int*format,?uint32_t?*channels,
uint32_t?*sample_rate,
audio_in_acoustics_t?acoustics,
structaudio_stream_in?**stream_in);
void(*close_input_stream)(structaudio_hw_device?*dev,
structaudio_stream_in?*in);
/**?This?method?dumps?the?state?of?the?audio?hardware?*/
int(*dump)(conststructaudio_hw_device?*dev,intfd);
};
typedefstructaudio_hw_device?audio_hw_device_t;
注:這是比較標準的C接口設計方法了,但是個人感覺還是用C++比較好,直觀易讀。2.3之前都是用C++實現這些接口設計的,到了4.0,不知道為何采納用C?不會理由是做底層的不懂C++吧?!三、Audio Hardware HAL的legacy實現
之前提到兩種Audio Hardware HAL接口定義:
1/ legacy:hardware/libhardware_legacy/include/hardware_legacy/AudioHardwareInterface.h
2/ 非legacy:hardware/libhardware/include/hardware/audio.h
前者是2.3及之前的音頻設備接口定義,后者是4.0的接口定義。
為了兼容以前的設計,4.0實現一個中間層:hardware/libhardware_legacy/audio/audio_hw_hal.cpp,結構與其他的audio_hw.c大同小異,差別在于open方法:
staticintlegacy_adev_open(consthw_module_t*?module,constchar*?name,
hw_device_t**?device)
{
......
ladev->hwif?=?createAudioHardware();
if(!ladev->hwif)?{
ret?=?-EIO;
gotoerr_create_audio_hw;
}
......
}看到那個熟悉的createAudioHardware()沒有?這是以前我提到的Vendor Specific Audio接口,然后新的接口再調用ladev->hwif的函數就是了。
因此老一套的alsa-lib、alsa-utils和alsa_sound也可以照搬過來,這里的文件被編譯成靜態庫的,因此你需要修改alsa_sound里面的Android.mk文件,鏈接這個靜態庫。還有alsa_sound的命名空間原來是“android”,現在需要改成“android_audio_legacy”。
四、a2dp Audio HAL的實現
4.0的a2dp audio hal放到bluez里實現了,我找了好一會才找到:
external/Bluetooth/bluez/audio/android_audio_hw.c
大致與上面提到的audio_hw.c類似,因為都是基于audio.h定義的接口來實現的。
如果需要編譯這個庫,須在BoardConfig.mk里定義:
BOARD_HAVE_BLUETOOTH := true
開始還提到現在支持3種audio設備了,分別是primary、a2dp和usb。目前剩下usb audio hal我沒有找到,不知是否需要自己去實現?其實alsa-driver都支持大部分的usb-audio設備了,因此上層也可調用tinyalsa的接口,就像samsung tuna的audio_hw.c那樣。
五、音質改進???
可使用audio echo cancel和更好的resampler(SRC)???
--to be continued…
總結
以上是生活随笔為你收集整理的android 4.0 电话录音,ANDROID音频系统散记之四:4.0音频系统HAL初探的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: x79内存条顺序:性能对比、兼容考量、选
- 下一篇: android按钮防止重复点击事件,实例