Audio系统和上层接口
在Android中,Audio系统自上而下由Java的Audio类、Audio本地框架类、AudioFlinger和Audio的硬件抽象层几个部分组成。
è 7.2.1 Audio系统的各个层次
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系统,输出输入两个类负责数据通道。在各个层次之间具有对应关系,如表7-1所示所示。
表7-1 Android各个层次的对应关系
| Audio管理环节 | Audio输出 | Audio输入 |
Java层 | android.media.AudioSystem | android.media.AudioTrack | android.media.AudioRecorder |
本地框架层 | AudioSystem | AudioTrack | AudioRecorder |
AudioFlinger | IAudioFlinger | IAudioTrack | IAudioRecorder |
硬件抽象层 | AudioHardwareInterface | AudioStreamOut | AudioStreamIn |
è 7.2.2 media库中的Audio框架部分
Android的Audio系统的核心框架在media库中提供,对上面主要实现AudioSystem、AudioTrack和AudioRecorder三个类。
提供了IAudioFlinger类接口,在这个类 中,可以获得IAudioTrack和IAudioRecorder两个接口,分别用于声音的播放和录制。AudioTrack和 AudioRecorder分别通过调用IAudioTrack和IAudioRecorder来实现。
Audio系统的头文件在frameworks/base/include/media/目录中,主要的头文件如下:
n AudioSystem.h:media库的Audio部分对上层的总管接口;
n IAudioFlinger.h:需要下层实现的总管接口;
n AudioTrack.h:放音部分对上接口;
n IAudioTrack.h:放音部分需要下层实现的接口;
n AudioRecorder.h:录音部分对上接口;
n IAudioRecorder.h:录音部分需要下层实现的接口。
IaudioFlinger.h、 IAudioTrack.h和IAudioRecorder.h这三个接口通过下层的继承来实现(即AudioFlinger)。 AudioFlinger.h、AudioTrack.h和AudioRecorder.h是对上层提供的接口,它们既供本地程序调用(例如声音的播放 器、录制器等),也可以通过JNI向Java层提供接口。
meida库中Audio部分的结构如图7-2所示。
图7-2 meida库中Audio部分的结构
从功能上看,AudioSystem负责的是Audio系统的综合管理功能,而AudioTrack和AudioRecorder分别负责音频数据的输出和输入,即播放和录制。
AudioSystem.h中主要定义了一些枚举值和set/get等一系列接口,如下所示:
class AudioSystem
{
public:
enumstream_type { //Audio 流的类型
SYSTEM = 1,
RING = 2,
MUSIC = 3,
ALARM = 4,
NOTIFICATION = 5,
BLUETOOTH_SCO = 6,
ENFORCED_AUDIBLE = 7,
NUM_STREAM_TYPES
};
enumaudio_output_type { // Audio数据输出类型
// …… 省略部分内容 };
enumaudio_format { // Audio数据格式
FORMAT_DEFAULT = 0,
PCM_16_BIT,
PCM_8_BIT,
INVALID_FORMAT
};
enumaudio_mode { // Audio模式
// …… 省略部分内容 };
enumaudio_routes { // Audio路径类型
ROUTE_EARPIECE = (1<< 0),
ROUTE_SPEAKER = (1<< 1),
ROUTE_BLUETOOTH_SCO = (1 <<2),
ROUTE_HEADSET = (1<< 3),
ROUTE_BLUETOOTH_A2DP = (1<< 4),
ROUTE_ALL = -1UL,
};
// …… 省略部分内容
staticstatus_t setMasterVolume(float value);
staticstatus_t setMasterMute(bool mute);
staticstatus_t getMasterVolume(float* volume);
staticstatus_t getMasterMute(bool* mute);
staticstatus_t setStreamVolume(int stream, float value);
staticstatus_t setStreamMute(int stream, bool mute);
staticstatus_t getStreamVolume(int stream, float* volume);
staticstatus_t getStreamMute(int stream, bool* mute);
staticstatus_t setMode(int mode);
static status_t getMode(int* mode);
staticstatus_t setRouting(int mode, uint32_t routes, uint32_t mask);
staticstatus_t getRouting(int mode, uint32_t* routes);
// …… 省略部分内容
};
在Audio系统的几个枚举值中,audio_routes是由单独的位 来表示的,而不是由顺序的枚举值表示,因此这个值在使用过程中可以使用“或”的方式。例如,表示声音可以既从耳机(EARPIECE)输出,也从扬声器 (SPEAKER)输出,这样是否能实现,由下层提供支持。在这个类中,set/get等接口控制的也是相关的内容,例如Audio声音的大小、 Audio的模式、路径等。
AudioTrack是Audio输出环节的类,其中最重要的接口是write(),主要的函数如下所示。
class AudioTrack
{
typedefvoid (*callback_t)(int event, void* user, void *info);
AudioTrack( int streamType,
uint32_t sampleRate = 0, // 音频的采样律
int format = 0, // 音频的格式(例如8位或者16位的PCM)
int channelCount = 0, // 音频的通道数
int frameCount = 0, // 音频的帧数
uint32_t flags = 0,
callback_t cbf = 0,
void* user = 0,
int notificationFrames = 0);
void start();
void stop();
void flush();
void pause();
void mute(bool);
ssize_t write(const void*buffer, size_t size);
// …… 省略部分内容
}
AudioRecord是Audio输入环节的类,其中最重要的接口为read(),主要的函数如下所示。
class AudioRecord
{
public:
AudioRecord(int streamType,
uint32_t sampleRate = 0, // 音频的采样律
int format = 0, // 音频的格式(例如8位或者16位的PCM)
int channelCount = 0, // 音频的通道数
int frameCount = 0, // 音频的帧数
uint32_t flags = 0,
callback_t cbf = 0,
void* user = 0,
int notificationFrames = 0);
status_t start();
status_t stop();
ssize_t read(void* buffer,size_t size);
// …… 省略部分内容
}
AudioTrack和AudioRecord的read/write函 数的参数都是内存的指针及其大小,内存中的内容一般表示的是Audio的原始数据(PCM数据)。这两个类还涉及Auido数据格式、通道数、帧数目等参 数,可以在建立时指定,也可以在建立之后使用set()函数进行设置。
在libmedia库中提供的只是一个Audio系统框架,AudioSystem、AudioTrack和 AudioRecord分别调用下层的IAudioFlinger、IAudioTrack和IAudioRecord来实现。另外的一个接口是 IAudioFlingerClient,它作为向IAudioFlinger中注册的监听器,相当于使用回调函数获取IAudioFlinger运行时 信息。
è 7.2.3 AudioFlinger本地代码
AudioFlinger是Audio系统的中间层,在系统中起到服务作用,它主要作为libmedia提供的Audio部分接口的实现,其代码路径为:
frameworks/base/libs/audioflinger
AudioFlinger的核心文件是AudioFlinger.h和AudioFlinger.cpp,提供了类AudioFlinger,这个类是一个IAudioFlinger的实现,其主要接口如下所示:
class AudioFlinger : public BnAudioFlinger, publicIBinder::DeathRecipient
{
public:
//…… 省略部分内容
virtualsp<IAudioTrack> createTrack( // 获得音频输出接口(Track)
pid_t pid,
int streamType,
uint32_tsampleRate,
int format,
intchannelCount,
int frameCount,
uint32_t flags,
constsp<IMemory>& sharedBuffer,
status_t*status);
// …… 省略部分内容
virtual status_t setMasterVolume(float value);
virtual status_t setMasterMute(bool muted);
virtual status_t setStreamVolume(int stream, float value);
virtual status_t setStreamMute(int stream, bool muted);
virtual status_t setRouting(int mode, uint32_t routes,uint32_t mask);
virtual uint32_t getRouting(int mode) const;
virtual status_t setMode(int mode);
virtual int getMode() const;
virtualsp<IAudioRecord> openRecord( // 获得音频输出接口(Record)
pid_t pid,
int streamType,
uint32_tsampleRate,
int format,
intchannelCount,
int frameCount,
uint32_t flags,
status_t*status);
}
AudioFlinger主要提供createTrack()创建音频的输出设备IAudioTrack,openRecord()创建音频的输入设备IAudioRecord。另外包含的就是一个get/set接口,用于控制。
AudioFlinger构造函数片段如下所示:
AudioFlinger::AudioFlinger()
{
mHardwareStatus = AUDIO_HW_IDLE;
mAudioHardware = AudioHardwareInterface::create();
mHardwareStatus = AUDIO_HW_INIT;
if(mAudioHardware->initCheck() == NO_ERROR) {
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
status_t status;
AudioStreamOut *hwOutput =
mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0,&status);
mHardwareStatus = AUDIO_HW_IDLE;
if(hwOutput) {
mHardwareMixerThread =
new MixerThread(this, hwOutput, AudioSystem::AUDIO_OUTPUT_HARDWARE);
} else{
LOGE("Failed to initialize hardware output stream, status:%d", status);
}
// …… 省略部分内容
mAudioRecordThread = new AudioRecordThread(mAudioHardware, this);
if(mAudioRecordThread != 0) {
mAudioRecordThread->run("AudioRecordThread",PRIORITY_URGENT_AUDIO);
}
} else {
LOGE("Couldn't even initialize the stubbed audio hardware!");
}
}
从工作的角度看,AudioFlinger在初始化之后,首先获得放音设备,然后为混音器(Mixer)建立线程,接着建立放音设备线程,在线程中获得放音设备。
在AudioFlinger的AudioResampler.h中定义了一个音频重取样器工具类,如下所示:
class AudioResampler {
public:
enumsrc_quality {
DEFAULT=0,
LOW_QUALITY=1, // 线性差值算法
MED_QUALITY=2, // 立方差值算法
HIGH_QUALITY=3 //fixed multi-tap FIR算法
};
staticAudioResampler* create(int bitDepth, int inChannelCount, // 静态地创建函数
int32_t sampleRate, int quality=DEFAULT);
virtual~AudioResampler();
virtualvoid init() = 0;
virtualvoid setSampleRate(int32_t inSampleRate); // 设置重采样率
virtualvoid setVolume(int16_t left, int16_t right); // 设置音量
virtualvoid resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider*provider) = 0;
};
这个音频重取样工具包含3种质量:低等质量 (LOW_QUALITY)将使用线性差值算法实现;中等质量(MED_QUALITY)将使用立方差值算法实现;高等质量(HIGH_ QUALITY)将使用FIR(有限阶滤波器)实现。AudioResampler中的AudioResamplerOrder1是线性实 现,AudioResamplerCubic.*文件提供立方实现方式,AudioResamplerSinc.*提供FIR实现。
AudioMixer.h和AudioMixer.cpp中实现的是一个Audio系统混音器,它被AudioFlinger调用,一般用于在声音输出之前的处理,提供多通道处理、声音缩放、重取样。AudioMixer调用了AudioResampler。
提示: AudioFlinger本身的实现通过调用下层的Audio硬件抽象层的接口来实现具体的功能,各个接口之间具有对应关系。
è 7.2.4 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个类的支持:
n android.media.AudioSystem:负责Audio系统的总体控制;
n android.media.AudioTrack:负责Audio系统的输出环节;
n android.media.AudioRecorder:负责Audio系统的输入环节。
在Android的Java层中,可以对Audio系统进行控制和数据流操作,对于控制操作,和底层的处理基本一致;但是对于数据流操作,由于Java不支持指针,因此接口被封装成了另外的形式。
例如,对于音频输出,android_media_AudioTrack.cpp提供的是写字节和写短整型的接口类型。
static jintandroid_media_AudioTrack_native_write(JNIEnv *env, jobject thiz,
jbyteArray javaAudioData,
jint offsetInBytes, jint sizeInBytes,
jint javaAudioFormat) {
jbyte*cAudioData = NULL;
AudioTrack*lpTrack = NULL;
lpTrack =(AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields. Native TrackInJavaObj);
// …… 省略部分内容
ssize_twritten = 0;
if(lpTrack->sharedBuffer() == 0) {
//进行写操作
written = lpTrack->write(cAudioData + offsetInBytes, sizeInBytes);
} else {
if(javaAudioFormat == javaAudioTrackFields.PCM16) {
memcpy(lpTrack->sharedBuffer()->pointer(),
cAudioData+offsetInBytes, sizeInBytes);
written = sizeInBytes;
} elseif (javaAudioFormat == javaAudioTrackFields.PCM8) {
int count = sizeInBytes;
int16_t *dst = (int16_t*)lpTrack->sharedBuffer()->pointer();
const int8_t *src = (const int8_t *)(cAudioData + offsetInBytes);
while(count--) {
*dst++ = (int16_t)(*src++^0x80) << 8;
}
written = sizeInBytes;
}
}
// …… 省略部分内容
env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0);
return(int)written;
}
所定义的JNI接口native_write_byte和native_write_short如下所示:
{"native_write_byte", "([BIII]I", (void *)android_media_AudioTrack_native_write),
{"native_write_short", "([SIII]I", (void *)android_media_AudioTrack_native_write_short),
向Java提供native_write_byte和native_write_short接口,它们一般是通过调用AudioTrack的write()函数来完成的,只是在Java的数据类型和C++的指针中做了一步 转换。
è 7.2.5 Audio系统的Java代码
Android的Audio系统的相关类在android.media 包中,Java部分的代码路径为:
frameworks/base/media/java/android/media
Audio系统主要实现了以下几个类:android.media.AudioSystem、 android.media.Audio Track、android.media.AudioRecorder、android.media.AudioFormat。前面的3个类和本地代码是 对应的,AudioFormat提供了一些Audio相关类型的枚举值。
注 意:在Audio系统的Java代码中,虽然可以通过AudioTrack和AudioRecorder的write()和read()接口,在Java 层对Audio的数据流进行操作。但是,更多的时候并不需要这样做,而是在本地代码中直接调用