Android音頻框架筆記 - 下篇

提醒一下,純個人筆記,你完全可能看暈

六、HAL層

6-1、Audio HAL層,其實包括了audio.xxx.so 和 audiopolicy.so等。從前述的總框架圖,也有寫,代碼庫路徑也有寫。

具體運行時so對象圖,對于audio.xxx.so部分,參考“Android系統Audio框架介紹”最后一張圖。如下:

Paste_Image.png

6-2、對audio.primary.so庫,對于Audio HAL框架的實現分析

Audio HAL層架構定義: hardware\libhardware\include\hardware\audio.h
廠商實現:以Anroid4.1.1版為例,audio.primary.grouper.so庫為例, 代碼位置device\asus\grouper\audio\audio_hw.c

//Audio HAL層架構中audio_module 的定義,繼承hw_module_t,其實沒有任何擴展

struct audio_module {
  struct hw_module_t common;
};

// 廠商audio_module 的實現,關鍵是open函數賦值,其作用是打開該設備,返回audio_hw_device 結構體對象,但在這份實現中,返回是audio_device對象,即audio_hw_device子類

struct audio_module HAL_MODULE_INFO_SYM = { 
    .common = {
      .tag = HARDWARE_MODULE_TAG,
      .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
      .hal_api_version = HARDWARE_HAL_API_VERSION,
      .id = AUDIO_HARDWARE_MODULE_ID,
      .name = "Grouper audio HW HAL",
      .author = "The Android Open Source Project",
      .methods = &hal_module_methods,
  },
};

static struct hw_module_methods_t hal_module_methods = {
    .open = adev_open, //open函數主要是填充audio_hw_device 結構體對象,返回之
};

//Audio HAL層架構中audio_hw_device的定義,audio_hw_device 繼承自hw_device_t。
//可以看到跟上層調用者的接口很一致,其實,它的作用就是為上層提供統一、穩定的接口,從而讓底層的頻繁變化,可以不影響上層的架構。

struct audio_hw_device {
    struct hw_device_t common;

    uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
    int (*init_check)(const struct audio_hw_device *dev);
    int (*set_voice_volume)(struct audio_hw_device *dev, float volume);
    int (*set_master_volume)(struct audio_hw_device *dev, float volume);
    int (*get_master_volume)(struct audio_hw_device *dev, float *volume);
    int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode);
    int (*set_mic_mute)(struct audio_hw_device *dev, bool state);
    int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state);
    int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
    char * (*get_parameters)(const struct audio_hw_device *dev,
                         const char *keys);
    size_t (*get_input_buffer_size)(const struct audio_hw_device *dev,
                                const struct audio_config *config);
    int (*open_output_stream)(struct audio_hw_device *dev,
                          audio_io_handle_t handle,
                          audio_devices_t devices,
                          audio_output_flags_t flags,
                          struct audio_config *config,
                          struct audio_stream_out **stream_out);
    void (*close_output_stream)(struct audio_hw_device *dev,
                            struct audio_stream_out* stream_out);
    int (*open_input_stream)(struct audio_hw_device *dev,
                         audio_io_handle_t handle,
                         audio_devices_t devices,
                         struct audio_config *config,
                         struct audio_stream_in **stream_in);
    void (*close_input_stream)(struct audio_hw_device *dev,
                           struct audio_stream_in *stream_in);
    int (*dump)(const struct audio_hw_device *dev, int fd);
};
typedef struct audio_hw_device audio_hw_device_t;

//廠商中audio_hw_device 的實現,audio_device 繼承自audio_hw_device。關鍵在于stream_out / stream_in 兩個類

struct audio_device {
    struct audio_hw_device hw_device;

    pthread_mutex_t lock; /* see note below on mutex acquisition order */
    unsigned int devices;
    bool standby;
    bool mic_mute;
    struct audio_route *ar;
    int orientation;
    bool screen_off;

    struct stream_out *active_out;
    struct stream_in *active_in;
};

// 以下作簡明闡述
//Audio HAL層架構中,audio_stream_out / audio_stream_in 代表輸出/輸入流
//這里是實現類,關鍵是struct pcm *pcm; 它已經就是alsa庫里定義的結構體了。它的初始化也正是調用alsa庫的open_pcm()函數而來。
// out->pcm = pcm_open(PCM_CARD, device, PCM_OUT | PCM_NORESTART, out->pcm_config);
//所以,意思就是,從這里開始,往下就是調用alsalib庫了。

struct stream_out {
    struct audio_stream_out stream;
    struct pcm *pcm;
    ...
};

struct stream_in {
    struct audio_stream_in stream;
    struct pcm *pcm;
   ...
};
audio設備打開流程

audio_policy.conf加載、解析之后的流程,見前面
audio_policy.conf加載、解析之后的流程如下:
廠商的Audio HAL模塊加載流程:
AudioFlinger::loadHwModule(const char *name)
-> load_audio_interface()
-> hw_get_module_by_class()
//參考HAL框架加載知識,這里加載到相應.so的xxx_hw_module_t 結構體
-> audio_hw_device_open() --> xxx_hw_module_t.open() ,在參數里返回audio_hw_device_t結構體

至此,已經加載了HAL模塊.so庫,并打開相應設備,返回我們要的audio_hw_device_t結構體
多少個.so,見audio_policy.conf加載上半部,
audio_hw_device_t: 音頻設備的抽象類。多少個audio_hw_device_t結構體? 有多少個so,就有多少個。 一一對應關系。
audio_stream_out : 音頻設備的輸入輸出通道的抽象類。這個才是對應pcmc0d0p之類的具體通道,right?
至于這些結構體的定義與功能,見HAL相關小節。

輸出通道打開流程:
  • 輸出通道打開流程(上)
    如AudioRecord.cpp開始:
    AudioRecord.cpp
    ->set()
    -> AudioSystem.getInput()
    -> 各種輾轉
    ->AudioFlinger->openInput()

如AudioTrack.cpp開始:
AudioTrack.set()
-> AudioSystem::getOutput
-> 各種輾轉
->AudioFlinger->openOutput()

  • 輸出通道打開流程(下)
    無論上層的打開流程如何輾轉反側,最終都調用到具體執行者AudioFlinger。由此開始分析如下:
    AudioFlinger如何打開輸出通道呢? 具體是如何調用到alsalib庫,甚至驅動的呢? 接著走。
    AudioFlinger->openOutput()
    -> findSuitableHwDev_l ()
    //尋找輸出設備,返回audio_hw_device_t
    -> audio_hw_device_t-> open_output_stream(..., struct audio_stream_out **stream_out))
    //HAL層,Audio架構標準接口
    -> new audio_stream_out
    //HAL層,廠商實現類,如device\asus\grouper\audio\audio_hw.c
    -> audio_stream_out.pcm = pcm_open()
    // pcm_open() 已經是調用到tinyalsa庫了,也就操作到了/dev/snd/pcmcxdxx設備節點了。
    //所以看到調用層級AudioFlinger -> Audio HAL -> tinyalsa -> ALSA driver -> 硬件。此即所謂軟件層級。

  • 參考
    “ANDROID音頻系統散記之四:4.0音頻系統HAL初探”

七、 TinyAlsa庫

直接看其Android.mk你就明白了:
libtinyalsa.so:
給Audio HAL層代碼調用的庫,即幾個audio.xxx.so會調用到libtinyalsa.so里的函數。
從框架全圖也可以看到。
其它:tinyalsa工具程序,可用于測試、調試

流程圖
初始化, 見書

八、調試

tinyalas調試

命令: tinyplay / tinycap /tinypcminfo (用法直接看usage)
命令源碼位置:/external/tinyalsa
聲卡驅動信息:cat /proc/asound/cards
tinyalsa聲卡設備:ls /dev/snd

九、接入第三方視頻解碼庫

mark而已,屬于視頻部分。

十、參考

ALSA官方文檔
http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/index.html
《深入理解Android內核設計思想》-2014
[深入理解Android卷一全文-第七章] 深入理解Audio系統
http://blog.csdn.net/innost/article/details/47208109

有不少這個模塊的文章,不過是2012年左右,ALSA架構時代,其中值得參考的兩篇已存
http://blog.csdn.net/njuitjf/article/category/837094/1

一些沒看,暫時的資料:
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27570663&id=4432842

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 提醒一下,純個人筆記,你完全可能看暈 一、音頻數字化基礎知識 見書,列出知識點如下: 聲音聲波,聲音頻率、響度, ...
    YY17閱讀 31,400評論 6 48
  • 一.聲音參數基本概念: 聲音是連續模擬量,計算機將它離散化之后用數字表示,就有了以下幾個名詞術語。 樣本長度(sa...
    cs1001閱讀 2,784評論 0 2
  • 一.聲音參數基本概念: 聲音是連續模擬量,計算機將它離散化之后用數字表示,就有了以下幾個名詞術語。 樣本長度(sa...
    cs1001閱讀 5,451評論 0 3
  • Android HAL概述 Android HAL(Hardware Abstract Layer)硬件抽象層,從...
    諾遠閱讀 30,475評論 2 27
  • Android FFmpeg音頻播放 本文介紹了使用opensl es和FFmpeg在Android平臺上實現音頻...
    JasonXiao閱讀 4,446評論 9 26