searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Android 音频 goldfish 学习

2023-11-30 01:25:42
41
0

简介

goldfish是google为android提供的一个模拟器。包含内核、aosp等部分。最近用到audio相关知识,学习下goldfish中的设计。
goldfish在hw的模拟在/device/generic/goldfish目录中,其中目录对应相应的模块(本文中列出的代码是aosp9中的)。

代码及关键点

查看模块从脚本开始

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_VENDOR_MODULE := true
LOCAL_MODULE := audio.primary.goldfish
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional

LOCAL_SHARED_LIBRARIES := libcutils liblog

LOCAL_SRC_FILES := audio_hw.c
.....

include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_VENDOR_MODULE := true
LOCAL_MODULE := audio.primary.goldfish_legacy
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional

LOCAL_SHARED_LIBRARIES := libcutils liblog

LOCAL_SRC_FILES := audio_hw_legacy.c
...
include $(BUILD_SHARED_LIBRARY)

从mk中可以看到包含两个module audio.primary.goldfish和 audio.primary.goldfish_legacy,legacy可以认为是老式的实现, in和out分别代表的音频的输入和输出。

对比legacy中对音频设备的描述:

legacy:
struct generic_audio_device {
    struct audio_hw_device device;
    pthread_mutex_t lock;
    struct audio_stream_out *output;
    struct audio_stream_in *input;
    int fd;
    bool mic_mute;
};

struct generic_audio_device {
    struct audio_hw_device device; // Constant after init
    pthread_mutex_t lock;
    bool mic_mute;                 // Proteced by this->lock
    struct mixer* mixer;           // Proteced by this->lock
};

stream的描述:

legacy:

struct generic_stream_out {
    struct audio_stream_out stream;
    struct generic_audio_device *dev;
    audio_devices_t device;
    uint32_t sample_rate;
};

struct generic_stream_in {
    struct audio_stream_in stream;
    struct generic_audio_device *dev;
    audio_devices_t device;
};


struct generic_stream_out {
    struct audio_stream_out stream;   // Constant after init
    pthread_mutex_t lock;
    struct generic_audio_device *dev; // Constant after init
    audio_devices_t device;           // Protected by this->lock
    struct audio_config req_config;   // Constant after init
    struct pcm_config pcm_config;     // Constant after init
    audio_vbuffer_t buffer;           // Constant after init

    // Time & Position Keeping
    bool standby;                      // Protected by this->lock
    uint64_t underrun_position;        // Protected by this->lock
    struct timespec underrun_time;     // Protected by this->lock
    uint64_t last_write_time_us;       // Protected by this->lock
    uint64_t frames_total_buffered;    // Protected by this->lock
    uint64_t frames_written;           // Protected by this->lock
    uint64_t frames_rendered;          // Protected by this->lock

    // Worker
    pthread_t worker_thread;          // Constant after init
    pthread_cond_t worker_wake;       // Protected by this->lock
    bool worker_standby;              // Protected by this->lock
    bool worker_exit;                 // Protected by this->lock
};

struct generic_stream_in {
    struct audio_stream_in stream;    // Constant after init
    pthread_mutex_t lock;
    struct generic_audio_device *dev; // Constant after init
    audio_devices_t device;           // Protected by this->lock
    struct audio_config req_config;   // Constant after init
    struct pcm *pcm;                  // Protected by this->lock
    struct pcm_config pcm_config;     // Constant after init
    int16_t *stereo_to_mono_buf;      // Protected by this->lock
    size_t stereo_to_mono_buf_size;   // Protected by this->lock
    audio_vbuffer_t buffer;           // Protected by this->lock

    // Time & Position Keeping
    bool standby;                     // Protected by this->lock
    int64_t standby_position;         // Protected by this->lock
    struct timespec standby_exit_time;// Protected by this->lock
    int64_t standby_frames_read;      // Protected by this->lock

    // Worker
    pthread_t worker_thread;          // Constant after init
    pthread_cond_t worker_wake;       // Protected by this->lock
    bool worker_standby;              // Protected by this->lock
    bool worker_exit;                 // Protected by this->lock
};

可以看出,legacy的实现先对简单,较新的实现,音频的读写采用了线程和vbuffer。至于vbuffer是什么?通过查看可以看出是个缓冲区:

typedef struct audio_vbuffer {
    pthread_mutex_t lock;
    uint8_t *  data;
    size_t     frame_size;
    size_t     frame_count;
    size_t     head;
    size_t     tail;
    size_t     live;
} audio_vbuffer_t;

两者的函数映射都是在dev_open中执行:

static int adev_open(const hw_module_t* module, const char* name,
                     hw_device_t** device)
{
   ....

    adev->device.common.tag = HARDWARE_DEVICE_TAG;
    adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
    adev->device.common.module = (struct hw_module_t *) module;
    adev->device.common.close = adev_close;

    adev->device.init_check = adev_init_check;               // no op
    adev->device.set_voice_volume = adev_set_voice_volume;   // no op
    adev->device.set_master_volume = adev_set_master_volume; // no op
    adev->device.get_master_volume = adev_get_master_volume; // no op
    adev->device.set_master_mute = adev_set_master_mute;     // no op
    adev->device.get_master_mute = adev_get_master_mute;     // no op
    adev->device.set_mode = adev_set_mode;                   // no op
    adev->device.set_mic_mute = adev_set_mic_mute;
    adev->device.get_mic_mute = adev_get_mic_mute;
    adev->device.set_parameters = adev_set_parameters;       // no op
    adev->device.get_parameters = adev_get_parameters;       // no op
    adev->device.get_input_buffer_size = adev_get_input_buffer_size;
    adev->device.open_output_stream = adev_open_output_stream;
    adev->device.close_output_stream = adev_close_output_stream;
    adev->device.open_input_stream = adev_open_input_stream;
    adev->device.close_input_stream = adev_close_input_stream;
    adev->device.dump = adev_dump;

    *device = &adev->device.common;

    adev->mixer = mixer_open(PCM_CARD);
    struct mixer_ctl *ctl;

    // Set default mixer ctls
    // Enable channels and set volume
    ...

    audio_device_ref_count++;

unlock:
    pthread_mutex_unlock(&adev_init_lock);
    return 0;
}

static int adev_open(const hw_module_t* module, const char* name,
                     hw_device_t** device)
{
   ......
    adev->device.common.tag = HARDWARE_DEVICE_TAG;
    adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
    adev->device.common.module = (struct hw_module_t *) module;
    adev->device.common.close = adev_close;

    adev->device.init_check = adev_init_check;
    adev->device.set_voice_volume = adev_set_voice_volume;
    adev->device.set_master_volume = adev_set_master_volume;
    adev->device.get_master_volume = adev_get_master_volume;
    adev->device.set_master_mute = adev_set_master_mute;
    adev->device.get_master_mute = adev_get_master_mute;
    adev->device.set_mode = adev_set_mode;
    adev->device.set_mic_mute = adev_set_mic_mute;
    adev->device.get_mic_mute = adev_get_mic_mute;
    adev->device.set_parameters = adev_set_parameters;
    adev->device.get_parameters = adev_get_parameters;
    adev->device.get_input_buffer_size = adev_get_input_buffer_size;
    adev->device.open_output_stream = adev_open_output_stream;
    adev->device.close_output_stream = adev_close_output_stream;
    adev->device.open_input_stream = adev_open_input_stream;
    adev->device.close_input_stream = adev_close_input_stream;
    adev->device.dump = adev_dump;

    *device = &adev->device.common;

    return 0;
}

实际读写的线程在out_write_worker和in_read_worker中可以看到相关的实现。采用线程明显效率高一些。

其他

  1. android HAL 接口
    音频的hal分为
  • Core HAL
    AudioFlinger(音频服务)用于播放音频和控制音频路由的主要 API。

  • Effects HAL
    主要用于控制音频效果,例如自动增益控制和噪声抑制。

  • Common HAL
    是为core HAL API和Effects HAL API 提供数据支持,定义了一些相关的数据结构。

根据官网上列出的音频hal接口版本:

   Android 13	7.1
   Android 12	7.0
   Android 11	6.0
   Android 10	5.0
   Android 9	4.0
   Android 8	2.0

可查看代码/hardware/interfaces/audio/目录下内容。

  1. 采样率转换
    Android中有些地方要用到重采样。例如MP3的采样率为44.1KHZ,但Android设备中需要支持48kHZ的音频。从44.1KHZ到48KHZ的转换就相当于重采样。
    参考官方的重采样特征包含以下:

    • 信号整体幅度的保存程度
    • 信号频率带宽的保存程度(受设备采样率的限制)
    • 通过重采样器的整体延迟时间
    • 有关频率的一致相位和群组延迟
    • 计算复杂度(以 CPU 周期或功耗表示)
    • 允许的源采样率和设备采样率的比率
    • 动态更改采样率比率的能力
    • 支持的数字音频采样格式
      常见的采样率有线性、立方、具有原始系数的 sinc和具有修订系数的 sinc。
0条评论
0 / 1000
张****龙
15文章数
0粉丝数
张****龙
15 文章 | 0 粉丝