一、HAL之框架
1. tiny4412上HAL框架
audio.primary.tiny4412.so文件的Makefile:
device/friendly-arm/common/libaudio/Android.mk
LOCAL_SRC_FILES:= AudioHardware.cpp LOCAL_MODULE := audio.primary.$(TARGET_DEVICE) #TARGET_DEVICE就是tiny4412,生成audio.primary.tiny4412.so,其中primary是在 /system/etc/audio_policy.conf中指定的module的名字。 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw LOCAL_STATIC_LIBRARIES:= libmedia_helper LOCAL_SHARED_LIBRARIES:= \ libutils \ liblog \ libhardware_legacy \ # 依賴這個庫 libtinyalsa \ # 依賴這個庫 libaudioutils LOCAL_WHOLE_STATIC_LIBRARIES := libaudiohw_legacy
libaudiohw_legacy依賴文件:
hardware/libhardware_legacy/audio/Android.mk LOCAL_SRC_FILES := \ AudioHardwareInterface.cpp \ audio_hw_hal.cpp # 4412的5.0版Android上是這個HAL,其它的可不一定了 LOCAL_MODULE := libaudiohw_legacy LOCAL_MODULE_TAGS := optional LOCAL_STATIC_LIBRARIES := libmedia_helper LOCAL_CFLAGS := -Wno-unused-parameter include $(BUILD_STATIC_LIBRARY)
所以庫文件audio.primary.tiny4412.so至少涉及:
audio_hw_hal.cpp (hardware/libhardware_legacy/audio/audio_hw_hal.cpp)
audioHardwareInterface.cpp (hardware/libhardware_legacy/audio/audioHardwareInterface.cpp)
audioHardware.cpp (device/friendly-arm/common/libaudio/audioHardware.cpp) 生成audio.primary.tiny4412.so
作用:
audio_hw_hal.cpp:向上提供接口的函數
audioHardwareInterface.cpp:向廠商制定訪問硬件接口的函數
audioHardware.cpp:廠商實現的訪問聲卡硬件的文件
2. 要先弄清楚其框架,才能看懂其代碼。
從圖片上可以看出,HAL這一層有audio的HAL,也有Audio_policy的HAL。對於Audio_policy的HAL我們不關系,因為它基本上已經廢棄了。
HAL要向上層提供統一的接口,操作硬件也可能有一組接口或一個類,抓住這兩點分析HAL框架。
3. 見audio_hw_hal.cpp,向上提供的接口就是struct audio_hw_device,它是對hw_device_t結構的封裝。
audio_hw_hal.cpp位於hardware/libhardware_legacy/audio下,既然是舊的,那么有沒有新的呢?在hardware/libhardware/modules/audio
下有個audio_hw.c文件,它就是新的Audio的HAL文件,但是在5.0板中沒有使用它,它里面的函數全部為空。在版本的Android8.0中可以看出,
這個文件同樣沒有實現,所以libhardware_legacy下的還是起主打作用。但是在Android8.0中的高通的HAL使用的是新的。
4. audio_hw_hal.cpp
a.向上提供接口struct audio_hw_device
b.向下訪問硬件class AudioHardware(實現在device/friendly-arm/common/libaudio/audioHardware.cpp) 由廠商提供,里面使用到了tinyalsa
庫的接口。但是廠商肯定不能隨意去實現硬件的訪問接口,Android指定了一套接口給它。
類的繼承關系:
AudioHardwareInterface //hardware/AudioHardwareInterface.cpp ↑ AudioHardwareBase //hardware/AudioHardwareBase.h 這個應該是audio HAL給廠商定義的接口 ↑ AudioHardware //device/friendly-arm/common/libaudio/audioHardware.cpp
5. 在廠商的HAL中,AudioHardware (audioHardware.cpp中)表示一個聲卡,它使用audio_stream_out結構來表示輸出,使用audio_stream_in來表示輸入。
audio_stream_out中有write(),audio_stream_in中有read()
6. Audio HAL的調用流程總結
上層應用程序調用audio_hw_hal.cpp中的legacy_adev_open()會得到一個struct audio_hw_device結構體,這個結構體就代表上層使用硬件的接口,這個結構體中有一堆的函數,它們都依賴於廠家提供的struct AudioHardwareInterface結構(在HAL中使用,在廠商代碼中實現,此例中是在audioHardware.cpp中實現的),由於HAL對上層直接提供的接口中沒有read/write函數,因此,應用程序想要錄音或播放聲音的時候需要先打卡output或input(調用HAL的open_output_stream/open_input_stream),以使用其里面的write/read函數向聲卡硬件寫音頻數據。
7. HAL相關的數據結構
struct legacy_audio_device { //規范了向上提供的接口 struct audio_hw_device device; //向上提供的接口依賴廠家提供的這個類來實現 struct AudioHardwareInterface *hwif; }; 下面的legacy_stream_out和legacy_stream_in也是同理 struct legacy_stream_out { struct audio_stream_out stream; AudioStreamOut *legacy_out; }; struct legacy_stream_in { struct audio_stream_in stream; AudioStreamIn *legacy_in; };
上面的也是Android源碼中常用的套路。
8. http://androidxref.com/ 上有各個版本的Android源碼,查看代碼非常方便
9. 由於有類的繼承關系,SourceInsight工程自動跳轉是不准的。
10. AudioFlinger啟動過程中,AudioPolicyService構造過程中會讀取配置文件 /system/etc/audio_policy.conf,該配置文件中有一個module名為"primary",根據這個名字, 會去 /system/lib/hw 中打開庫文件: audio.primary.XXX.so,比如: audio.primary.tiny4412.so
11. 框架詳細描述
audio HAL中:
b.1 向上提供統一接口: audio_hw_device,里面設置了各種函數
b.2 這些函數要借助聲卡的代碼才能實現,聲卡的功能抽象為AudioHardware對象
b.3 audio_hw_device中的函數只看到各種set, get,沒有看到wirte, read,怎么播放聲音、錄制聲音?
b.4 要播放聲音,
b.4.1 要先open output:在audio_hw_device中有open_output_stream函數指針
b.4.2 open_output_stream返回一個audio_stream_out結構體,它是向上提供的"播放接口"
b.4.3 audio_stream_out也需要廠家提供的代碼才能實現,廠家提供的代碼抽象為AudioStreamOutALSA對象
b.5 對於錄制聲音
b.5.1 要先open input:在audio_hw_device中有open_input_stream函數指針
b.5.2 open_input_stream返回一個audio_stream_in結構體,它是向上提供的"錄制接口"
b.5.3 audio_stream_in也需要廠家提供的代碼才能實現,廠家提供的代碼抽象為AudioStreamInALSA對象
二、HAL之調用流程源碼分析
1. HAL文件一般位於/system/lib/hardware下面,音頻中操作硬件的庫文件名稱在(廠商提供的HAL部分)在/system/etc/policy_config中指定,如module name為primary,
就對應audio.primary.tiny4412.so。
2. 直接使用系統調用控制聲卡的是tinyalsa庫,位於目錄/external/tinyalsa下,編譯生成庫文件libtinyalsa.so(只涉及兩個文件mixer.c,pcm.c),編譯生成工具 tinycap,tinymix,tinypcminfo,tinyplay,可用於直接控制音頻通道,進行錄音播音測試。
tinyalsa中使用
pcm_open()來打開聲卡
pcm_write()來播放音樂
pcm_read()來錄音
3. HAL調用流程源碼分析
a. 確定HAL文件的名字:
AudioPolicyManager的構造函數讀取配置文件/system/etc/audio_policy.conf確定名字(這個名字在tiny4412上只是HAL廠商部分編譯生成庫的名字,HAL入口是hardware/libhardware_legacy/audio/audio_hw_hal.cpp)
b. 加載HAL對應的so文件。
c. 打開HAL文件中的open函數,在HAL中會構造audio_hw_device結構體, 該結構體中有各類函數, 特別是 open_output_stream / open_input_stream
AudioFlinger根據audio_hw_device結構體構造一個AudioHwDev對象並放入mAudioHwDevs
d. open output stream: 調用hal結構體audio_hw_device的open_output_stream, 它會構造出一個audio_stream_out結構體, 里面有write函數
AudioFlinger根據audio_stream_out結構體構造一個MixerThread
e. 播放聲音(這時才真正打開聲卡驅動):
thread->threadLoopg_write => stream.write ==> pcm_open, pcm_write