【初学音频】Android的Audio系统

  • Post author:
  • Post category:其他



目录


1.综述


2. Audio系统和上层接口


2.1 Audio系统的层次


2.2 media 库中的 Audio 框架部分


2.3 Audio 系统的 JNI 代码


2.4 Audio 系统的 Java 代码


3. Audio 的硬件抽象层


3.1 Audio 硬件抽象层的接口定义


3.2 AudioFlinger 中自带 Audio 硬件抽象层实现


3.2.1 用桩实现的 Audio 硬件抽象层


3.2.2 Android 通用的 Audio 硬件抽象层


3.2.3 提供 Dump 功能的 Audio 硬件抽象层


3.3 Audio 硬件抽象层的真正实现


1.综述

Audio系统是Android平台的重要组成部分,它主要包括三方面的内容:

  1. AudioRecorder和AudioTrack (AT) :这两个类属于Audio系统对外提供的API类,通过它们可以完成Android平台上音频数据的采集和输出任务。
  2. AudioFlinger (AF) :它是Audio系统的工作引擎,管理着系统中的输入输出音频流,并承担音频数据的混音以及读写Audio硬件实现数据的输入输出等工作。AudioFlinger是整个音频系统的核心与难点,作为Android系统中的音频中枢,它同时也是一个系统服务,启到承上 (为上层提供访问接口) 启下 (通过HAL来管理音频设备) 的作用。
  3. AudioPolicyService (AP) :它是Audio系统的策略控制中心,具有掌管系统中声音设备的选择和切换、音量控制等功能。

在这里插入图片描述


Audio 系统在 Android 中负责音频方面的数据流传输和控制功能,也负责音频设备的管理。这个部分作为 Android 的 Audio 系统的输入/输出层次,一般负责播放 PCM 声音输出和从外部获取 PCM 声音,以及管理声音设备和设置。

Audio 系统主要分成如下几个层次:

  1. media 库提供的 Audio 系统本地部分接口,作为本地部分的客户端;
  2. AudioFlinger 作为 Audio 系统的中间层,是audio的本地服务,他会打开hal层的动态库;
  3. Audio 的硬件抽象层提供底层支持,供AudioFlinger调用,他通过open、ioctl、close操作驱动的sysfs节点;
  4. Audio 接口通过 JNI 和 Java 框架提供给上层;
  5. JAVA接口层提供APK编程的接口;
  6. JAVA服务层为JAVA客户端提供服务;
  7. JNI层向上为java层提供接口,向下调用media库;
  8. Audio驱动,注册sysfs节点,并将所有的功能以ioctl 命令的方式实现。

系统结构如图所示:

在这里插入图片描述


  1. 应用层是提供音频相关的应用,比如播放器、电话、录音器等;

  2. JAVA


    层提供APK编程的接口,为JAVA客户端提供服务。比如AudioRecord是录音接口,AudioTrack是音频播放接口,AudioService是系统服务,提供应用所需的除了播放的相关的audio业务,与之对应的Client端是应用进程中的AudioManager。两者之间通过binder进行通信。

  3. JNI


    层向上为java层提供接口,向下调用media库,是连接java层和native层的桥梁;

  4. 本地框架层可以看成是两部分组成,client部分是与java层是一一对应的,Service部分包括策略制定者AudioPolicyService和策略执行者AudioFlinger;

  5. Hal


    层将与硬件相关的接口抽象出来代到上层应用,是AudioFlinger向下访问的对象。

Android 系统的

代码分布情况

如下所示:


(1)Audio 的 Java 部分



代码路径:frameworks/base/media/java/android/media


与 Audio 相关的 Java 包是 android.media,主要包含 AudioManager 和 Audio 系统的几个类。


(2)Audio 的 JNI 部分



代码路径:frameworks/base/core/jni


生成库 libandroid_runtime.so,Audio 的 JNI 是其中的一个部分。


(3)Audio 的框架部分



头文件路径:frameworks/base/include/media/



源代码路径:frameworks/base/media/libmedia/


Audio 本地框架是 media 库的一部分,本部分内容被编译成库 libmedia.so,提供 Audio 部分的接口(包括基于 Binder 的 IPC 机制)。


(4)Audio Flinger



代码路径:frameworks/base/libs/audioflinger


这部分内容被编译成库 libaudioflinger.so,它是 Audio 系统的本地服务部分。


(5)Audio 的硬件抽象层接口



头文件路径:hardware/libhardware_legacy/include/hardware/


Audio 硬件抽象层的实现在各个系统中可能是不同的,需要使用代码去继承相应的类并实现它们,作为 Android 系统本地框架层和驱动程序接口。

2. Audio系统和上层接口



2.1 Audio系统的层次

在 Android 中,Audio 系统自上而下由 Java 的 Audio 类、Audio 本地框架类、 AudioFlinger 和 Audio 的硬件抽象层几个部分组成。

Audio 本地框架类是 libmedia.so 的一个部分,这些 Audio 接口对上层提供接口,由下层的本地代码去实现。

AudioFlinger 继承 libmeida 中的接口,提供实现库 libaudiofilnger.so。这部分内容没有自己的对外头文件,上层调用的只是 libmedia 本部分的接口,但实际调用的内容是 libaudioflinger.so。

Audio 使用 JNI 和 Java 对上层提供接口,JNI 部分通过调用 libmedia 库提供的接口来实现。

Audio 的硬件抽象层提供到硬件的接口,供 AudioFlinger 调用。Audio 的硬件抽象层实际上是各个平台开发过程中需要主要关注和独立完成的部分。

提示:Android 的 Audio 系统不涉及编解码环节,只是负责上层系统和底层 Audio 硬件的交互,一般以 PCM 作为输入/输出格式。

在 Android 的 Audio 系统中,无论上层还是下层,都使用一个管理类和输出输入两个类来表示整个 Audio 系统,输出输入两个类负责数据通道。

2.2 media 库中的 Audio 框架部分

Android 的 Audio 系统的核心框架在 media 库中提供,对上面主要实现 AudioSystem、AudioTrack 和 AudioRecorder 三个类。


Audio管理环节

Audio输出

Audio输入

Java层
android.media. AudioSystem android.media AudioTrack android.media. AudioRecorder

本地框架层
AudioSystem AudioTrack AudioRecorder

AudioFlinger
IAudioFlinger IAudioTrack IAudioRecorder

硬件抽象层
AudioHardwareInterface AudioStreamOut AudioStreamIn

提供了 IAudioFlinger 类接口,在这个类中,可以获得 IAudioTrack 和 IAudioRecorder 两个接口,分别用于声音的播放和录制。AudioTrack 和 AudioRecorder 分别通过调用 IAudioTrack 和 IAudioRecorder 来实现。

Audio 系统的头文件在 frameworks/base/include/media/目录中,主要的头文件如下:

AudioSystem.h:media 库的 Audio 部分对上层的总管接口;

IAudioFlinger.h:需要下层实现的总管接口;

AudioTrack.h:放音部分对上接口;

IAudioTrack.h:放音部分需要下层实现的接口;

AudioRecorder.h:录音部分对上接口;

IAudioRecorder.h:录音部分需要下层实现的接口。

IAudioFlinger.h、IAudioTrack.h 和 IAudioRecorder.h 这三个接口通过下层的继承来实现(即 AudioFlinger)。AudioFlinger.h、AudioTrack.h 和 AudioRecorder.h 是对上层提供的接口,它们既供本地程序调用(例如声音的播放器、录制器等),也可以通过 JNI 向 Java 层提供接口。

从功能上看,AudioSystem 负责的是 Audio 系统的综合管理功能,而 AudioTrack 和AudioRecorder 分别负责音频数据的输出和输入,即播放和录制。

在 libmedia 库中提供的只是一个 Audio 系统框架,AudioSystem、 AudioTrack 和 AudioRecord 分别调用下层的 IAudioFlinger、 IAudioTrack 和 IAudioRecord 来实现。另外的一个接口是 IAudioFlingerClient,它作为向 IAudioFlinger 中注册的监听器,相当于使用回调函数获取 IAudioFlinger 运行时信息。

2.3 Audio 系统的 JNI 代码

Android 的 Audio 部分通过 JNI 向 Java 层提供接口,在 Java 层可以通过 JNI 接口完成 Audio 系统的大部分操作。

Audio JNI 部分的代码路径为:frameworks/base/core/jni。

其中,主要实现的 3 个文件为:android_media_AudioSystem.cpp、android_media_Audio Track.cpp 和 android_media_AudioRecord.cpp,它们分别对应了 Android Java 框架中的 3 个类的支持:

  • android.media.AudioSystem:负责 Audio 系统的总体控制;
  • android.media.AudioTrack:负责 Audio 系统的输出环节;
  • android.media.AudioRecorder:负责 Audio 系统的输入环节。

在 Android 的 Java 层中,可以对 Audio 系统进行控制和数据流操作,对于控制操作,和底层的处理基本一致;但是对于数据流操作,由于 Java 不支持指针, 因此接口被封装成了另外的形式。

2.4 Audio 系统的 Java 代码

Android 的 Audio 系统的相关类在 android.media 包中,Java 部分的代码路径为: frameworks/base/media/java/android/mediaAudio

系统主要实现了以下几个类:android.media.AudioSystem、android.media. Audio Track、android.media.AudioRecorder、android.media.AudioFormat。

前面的 3 个类和本地代码是对应的,AudioFormat 提供了一些 Audio 相关类型的枚举值。

注意:在 Audio 系统的 Java 代码中,虽然可以通过 AudioTrack 和 AudioRecorder 的 write()和 read()接口,在 Java 层对 Audio 的数据流进行操作。但是,更多的时候并不需要这样做,而是在本地代码中直接调用接口进行数据流的输入/输出,而 Java 层只进行控制类操作,不处理数据流。

3. Audio 的硬件抽象层

3.1 Audio 硬件抽象层的接口定义

Audio 的硬件抽象层是 AudioFlinger 和 Audio 硬件的接口,在各个系统的移植过程中可以有不同的实现方式。

Audio 硬件抽象层的接口路径为: hardware/libhardware_legacy/include/hardware/

其中主要的文件为:AudioHardwareBase.h 和 AudioHardwareInterface.h

Android 中的 Audio 硬件抽象层可以基于 Linux 标准的 ALSA 或 OSS 音频驱动实现,也可以基于私有的 Audio 驱动接口来实现。

在 AudioHardwareInterface.h 中定义了类:AudioStreamOut、AudioStreamIn 和 AudioHardwareInterface。

AudioStreamOut 和 AudioStreamIn 的主要定义如下所示:

[AudioHardwareInterface.h]

class AudioStreamOut {
public:
    virtual             ~AudioStreamOut() = 0;

    virtual status_t    setVolume(float left, float right) = 0;

    virtual ssize_t     write(const void* buffer, size_t bytes) = 0;

.....
};
[AudioHardwareInterface.h]

class AudioStreamIn {
public:
    virtual             ~AudioStreamIn() = 0;

    virtual status_t    setGain(float gain) = 0;

    virtual ssize_t     read(void* buffer, ssize_t bytes) = 0;

......
};

AudioStreamOut 和 AudioStreamIn 分别对应了音频的输出环节和输入环节,其中负责数据流的接口分别是 wirte()和 read(),参数是一块内存的指针和长度; 另外还有一些设置和获取接口。

Audio 的硬件抽象层主体 AudioHardwareInterface 类的定义如下所示:

class AudioHardwareInterface
{
public:
        virtual status_t initCheck() = 0;
        virtual status_t setVoiceVolume(float volume) = 0;
        virtual status_t setMasterVolume(float volume) = 0;
        virtual status_t setRouting(int mode, uint32_t routes) = 0;
        virtual status_t setMode(int mode) = 0;
        virtual status_t getMode(int* mode) = 0;
        
        ...... 
        
        virtual AudioStreamOut* openOutputStream( // 打开输出流
        int format=0,
        int channelCount=0,
        uint32_t sampleRate=0,
        status_t *status=0) = 0;
        
        virtual AudioStreamIn* openInputStream( // 打开输入流
        int format,
        int channelCount,
        uint32_t sampleRate,
        status_t *status,
        AudioSystem::audio_in_acoustics acoustics) = 0;
        static AudioHardwareInterface* create();
};

在这个 AudioHardwareInterface 接口中,使用 openOutputStream()和 openInputStream()函数分别获取 AudioStreamOut 和 AudioStreamIn 两个类,它们作为音频输入/输出设备来使用。

此外,AudioHardwareInterface.h 定义了 C 语言的接口来获取一个 AudioHardwareInterface 类型的指针。

extern "C" AudioHardwareInterface* createAudioHardware(void);

如果实现一个 Android 的硬件抽象层,则需要实现 AudioHardwareInterface、 AudioStreamOut 和 AudioStreamIn 三个类,将代码编译成动态库 libaudio.so。 AudioFlinger 会连接这个动态库,并调用其中的 createAudioHardware()函数来获取接口。

在 AudioHardwareBase.h 中定义了类:AudioHardwareBase,它继承了 AudioHardwareInterface,显然继承这个接口也可以实现 Audio 的硬件抽象层。

提示:Android 系统的 Audio 硬件抽象层可以通过继承类 AudioHardwareInterface 来实现,其中分为控制部分和输入/输出处理部分。

3.2 AudioFlinger 中自带 Audio 硬件抽象层实现

在 AudioFlinger 中可以通过编译宏的方式选择使用哪一个 Audio 硬件抽象层。 这些 Audio 硬件抽象层既可以作为参考设计,也可以在没有实际的 Audio 硬件抽 象层(甚至没有 Audio 设备)时使用,以保证系统的正常运行。

在 AudioFlinger 的编译文件 Android.mk 中,具有如下的定义:

ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
    LOCAL_STATIC_LIBRARIES += libaudiointerface
else
    LOCAL_SHARED_LIBRARIES += libaudio
endif
LOCAL_MODULE:= libaudioflinger
include $(BUILD_SHARED_LIBRARY)

定义的含义为:当宏 BOARD_USES_GENERIC_AUDIO 为 true 时,连接 libaudiointerface.a 静态库;当 BOARD_USES_GENERIC_AUDIO 为 false 时,连接 libaudiointerface.so 动态库。

在正常的情况下,一般是使用

后者(这里有疑问,按照文中意思不应该是一般使用前者吗)

,即在另外的地方实现 libaudiointerface.so 动态库,由 AudioFlinger 的库 libaudioflinger.so 来连接使用。

libaudiointerface.a 也在这个 Android.mk 中生成:

include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
        AudioHardwareGeneric.cpp \
        AudioHardwareStub.cpp \
        AudioDumpInterface.cpp \
        AudioHardwareInterface.cpp
LOCAL_SHARED_LIBRARIES := \
        libcutils \
        libutils \
        libmedia \
        libhardware_legacy
ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
    LOCAL_CFLAGS += -DGENERIC_AUDIO
endif
LOCAL_MODULE:= libaudiointerface
include $(BUILD_STATIC_LIBRARY)

以上内容通过编译 4 个源文件,生成了 libaudiointerface.a 静态库。其中 AudioHard wareInterface.cpp 负责实现基础类和管理,而 AudioHardwareGeneric.cpp、AudioHard wareStub.cpp 和 AudioDumpInterface.cpp 三个文件各自代表一种 Auido 硬件抽象层的实现。

  • AudioHardwareGeneric.cpp:实现基于特定驱动的通用 Audio 硬件抽象层;
  • AudioHardwareStub.cpp:实现 Audio 硬件抽象层的一个桩;
  • AudioDumpInterface.cpp:实现输出到文件的 Audio 硬件抽象层。

在AudioHardwareInterface.cpp 中,实现了 Audio 硬件抽象层的创建函数 AudioHardwareInterface::create(),内容如下所示:

AudioHardwareInterface* AudioHardwareInterface::create()
{
        AudioHardwareInterface* hw = 0;
        char value[PROPERTY_VALUE_MAX];
#ifdef GENERIC_AUDIO
        hw = new AudioHardwareGeneric();
// 使用通用的 Audio 硬件抽象层
#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();
// 使用实际的 Audio 硬件抽象层
        }
#endif
        if (hw->initCheck() != NO_ERROR) {
            LOGW("Using stubbed audio hardware.No sound will be produced.");
            delete hw;
            hw = new AudioHardwareStub();
// 使用实际的 Audio 硬件抽象层的桩实现
        }
#ifdef DUMP_FLINGER_OUT
            hw = new AudioDumpInterface(hw);
// 使用实际的 Audio 的 Dump 接口实现
#endif
            return hw;
}

根据 GENERIC_AUDIO、DUMP_FLINGER_OUT 等宏选择创建几个不同的 Audio 硬件抽象层,最后返回的接口均为 AudioHardwareInterface 类型的指针。

3.2.1 用桩实现的 Audio 硬件抽象层

AudioHardwareStub.h 和 AudioHardwareStub.cpp 是一个 Android 硬件抽象层的

桩实现方式

。这个实现不操作实际的硬件和文件,它所进行的是空操作,在系统没有实际的 Audio 设备时使用这个实现,来保证系统的正常工作。如果使用这个硬件抽象层,实际上 Audio 系统的输入和输出都将为空。

在实现过程中,为了保证声音可以输入和输出,这个桩实现的主要内容是实现AudioStreamOutStub 和 AudioStreamInStub 类的读/写函数。

使用这个接口进行音频的输入和输出时,和真实的设备没有关系,输出和输入都使用延时来完成。对于输出的情况,不会有声音播出,但是返回值表示全部内容已经输出完成;对于输入的情况,将返回全部为 0 的数据。

此外,这种实现支持默认的参数,如果用 set()函数设置的参数与默认参数不一致,还会返回错误。

3.2.2 Android 通用的 Audio 硬件抽象层

AudioHardwareGeneric.h 和 AudioHardwareGeneric.cpp 是 Android 通用的一个 Audio 硬件抽象 层。与前面的桩实现不同,这是一个真正能够使用的 Audio 硬件抽象层,但是它需要 Android 的一种特殊的声音驱动程序的支持。

与前面类似,AudioStreamOutGeneric、AudioStreamInGeneric 和 AudioHardwareGeneric 这 3 个类分别继承 Audio 硬件抽象层的 3 个接口。

在 AudioHardwareGeneric.cpp 的实现中,使用的驱动程序是/dev/eac,这是一个非标准程序,定义设备的路径如下所示:

static char const * const kAudioDeviceName = "/dev/eac";

对于 Linux 操作系统,这个驱动程序在文件系统中的节点主设备号为 10,次设备号自动生成。

提示:eac 是 Linux 中的一个 misc 驱动程序,作为 Android 的通用音频驱动, 写设备表示放音,读设备表示录音。

在 AudioHardwareGeneric 的构造函数中,打开这个驱动程序的设备节点。

AudioHardwareGeneric::AudioHardwareGeneric()
        : mOutput(0), mInput(0), mFd(-1), mMicMute(false)
{
        mFd = ::open(kAudioDeviceName, O_RDWR); //打开通用音频设备的节点
}

这个音频设备是一个比较简单的驱动程序,没有很多设置接口,只是用写设备表 示录音,读设备表示放音。放音和录音支持的都是 16 位的 PCM。

ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes)
{
    Mutex::Autolock _l(mLock);
    return ssize_t(::write(mFd, buffer, bytes)); //写入硬件设备
}
ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes)
{
    AutoMutex lock(mLock);
    if (mFd < 0) {
        return NO_INIT;
    }
    return ::read(mFd, buffer, bytes); // 读取硬件设备
}
 

虽然 AudioHardwareGeneric 是一个可以真正工作的 Audio 硬件抽象层,但是这种实现方式非常简单,不支持各种设置,参数也只能使用默认的。而且,这种驱动程序需要在 Linux 核心加入 eac 驱动程序的支持。

3.2.3 提供 Dump 功能的 Audio 硬件抽象层

AudioDumpInterface.h 和 AudioDumpInterface.cpp 是一个提供了 Dump 功能(

备份文件系统

)的 Audio 硬件抽象层,它所起到的作用就是将输出的 Audio 数据写入到文件中。

AudioDumpInterface 只实现了 AudioStreamOut,没有实现 AudioStreamIn,所以仅支持 Audio 的输出功能,不支持输入功能。

输出文件的名称被定义为:

#define FLINGER_DUMP_NAME "/data/FlingerOut.pcm"

在 AudioDumpInterface.cpp 的 AudioStreamOut 所实现的写函数中,写入的对象 就是这个文件。

ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes)
{
    ssize_t ret;
    ret = mFinalStream->write(buffer, bytes);
    if(!mOutFile && gFirst) {
        gFirst = false;
        mOutFile = fopen(FLINGER_DUMP_NAME, "r");
        if(mOutFile) {
            fclose(mOutFile);
            mOutFile = fopen(FLINGER_DUMP_NAME, "ab");    // 打开输出文件
        }
    }
    if (mOutFile) {
        fwrite(buffer, bytes, 1, mOutFile);   // 写文件输出内容
    }
    return ret;
}

如果文件是打开的,则使用追加方式写入。因此使用这个 Audio 硬件抽象层时, 播放的内容(PCM)将全部被写入文件。而且这个类支持各种格式的输出,这取决于调用者的设置。

AudioDumpInterface 并不是为了实际的应用使用的,而是为了调试使用的类。当进行音频播放器调试时,有时无法确认是解码器的问题还 是 Audio 输出单元的问题,这时就可以用这个类来替换实际的 Audio 硬件抽象层,将解码器输出的 Audio 的 PCM 数据写入文件中,由此可以判断解码 器的输出是否正确。

提示:使用 AudioDumpInterface 音频硬件抽象层,可以通过 /data/FlingerOut.pcm 文件找到 PCM 的输出数据。

3.3 Audio 硬件抽象层的真正实现

实现一个真正的 Audio 硬件抽象层,需要完成的工作和实现以上的硬件抽象层类似。

例如:可以基于 Linux 标准的音频驱动:OSS(Open Sound System)或者 ALSA (Advanced Linux Sound Architecture)驱动程序来实现。

对于 OSS 驱动程序,实现方式和前面的 AudioHardwareGeneric 类似,数据流的读/写操作通过对/dev/dsp 设备的读/写来完成;区别在于 OSS 支持了更多的 ioctl 来进行设置,还涉及通过/dev/mixer 设备进行控制,并支持更多不同的参数。

对于 ALSA 驱动程序,实现方式一般不是直接调用驱动程序的设备节点,而是先实现用户空间的 alsa-lib,然后 Audio 硬件抽象层通过调用 alsa-lib 来实现。

在实现 Audio 硬件抽象层时,对于系统中有多个 Audio 设备的情况,可由硬件抽 象层自行处理 setRouting()函数设定,例如,可以选择 支持多个设备的同时输出,或者有优先级输出。对于这种情况,数据流一般来自 AudioStreamOut::write()函数,可由硬件抽象层确定输出方法。对于某种特殊的情况,也有可能采用硬件直接连接的方式,此时数据流可能并不来自上面的 write(),这样就没有数据通道,只有控制接口。 Audio 硬件抽象层也是可以处理这种情况的。



版权声明:本文为weixin_45099376原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。