上篇说到语音部分最后会通过AudioFlinger来操作HAL层。
一、首先我们看下硬件接口层的接口(奇怪为什么只有Audio的hardwareinterface):
(1)hardware\libhardware_legacy\include\hardware_legacy\AudioHardwareInterface.h
其中定义了AudioStreamOut和AudioStreamIn,二者被AudioHardwareInterface类中的openOutputStream和openInputStream操作以对硬件音频的输入和输出进行管理。
类中定义的都是抽象函数,具体要实现Audio系统时去实现。
(2)hardware\libhardware_legacy\include\hardware_legacy\AudioPolicyInterface.h
这里定义了Android的策略类。
二、然后看下HAL层的实现
Android的HAL中提供三种示例:AudioHardwareStub AudioDumpInterface AudioHardwareGeneric这三种各自代表一种Audio硬件抽象层的实现
(1)AudioHardwareStub是一个空实现,不表述,可以自己去看代码
(2)AudioDumpInterface是一个用文件来模拟输入输出的示例
这个要先介绍AudioDumpInterface:
分析代码是上面的类关系,可以知道,在AudioDumpInterface中是new了AudioStreamInDump和AudioStreamOutDump两个对象来进行输入输出操作。
其中的AudioStreamInDump.write会生成一个PCM文件,而read函数能够将一个一个指定的Audio文件读入,比如Android5.0中是打开一个/sdcard/music/目录下的.wav文件
怎么命名参见程序实现:
ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes)
{
ssize_t ret;
if (mFinalStream) {
ret = mFinalStream->read(buffer, bytes);
if(!mFile) {
if (mInterface->fileName() != "") {
char name[255];
sprintf(name, "%s_in_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
mFile = fopen(name, "wb");
ALOGV("Opening input dump file %s, fh %p", name, mFile);
}
}
if (mFile) {
fwrite(buffer, bytes, 1, mFile);
}
} else {
usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000);
ret = bytes;
if(!mFile) {
char name[255];
strcpy(name, "/sdcard/music/sine440");
if (channels() == AudioSystem::CHANNEL_IN_MONO) {
strcat(name, "_mo");
} else {
strcat(name, "_st");
}
if (format() == AudioSystem::PCM_16_BIT) {
strcat(name, "_16b");
} else {
strcat(name, "_8b");
}
if (sampleRate() < 16000) {
strcat(name, "_8k");
} else if (sampleRate() < 32000) {
strcat(name, "_22k");
} else if (sampleRate() < 48000) {
strcat(name, "_44k");
} else {
strcat(name, "_48k");
}
strcat(name, ".wav");
mFile = fopen(name, "rb");
ALOGV("Opening input read file %s, fh %p", name, mFile);
if (mFile) {
fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
}
}
if (mFile) {
ssize_t bytesRead = fread(buffer, bytes, 1, mFile);
if (bytesRead >=0 && bytesRead < bytes) {
fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mFile);
}
}
}
return ret;
}
(3)AudioHardwareGeneric是一个真正和硬件交互的实现
3.1先在构造函数中指定了一个音频设备节点:kAudioDeviceName = "/dev/eac"
AudioHardwareGeneric::AudioHardwareGeneric()
: mOutput(0), mInput(0), mFd(-1), mMicMute(false)
{
mFd = ::open(kAudioDeviceName, O_RDWR);
}
3.2然后在openInputStream和openOutStream中创建输入输出对象的时候会将文件句柄传进去,
// create new output stream
AudioStreamInGeneric* in = new AudioStreamInGeneric();
status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics);
3.3最后就是在write和read中去读写文件节点了。
ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes)
{
Mutex::Autolock _l(mLock);
return ssize_t(::write(mFd, buffer, bytes));
}
实际使用的时候,可以由Android层的系统工程师和驱动工程师配合工作,驱动工程师完成音频驱动后,给出音频数据的节点,Android系统层去对这个设备节点进行读写。或者与其他网络模块比如蓝牙模块工作的时候,可以不通过内核。遥控器端将数据加密,编码压缩后,通过蓝牙部分接受裸数据,然后蓝牙模块通过socket传给语音模块进行解码,然后给上层使用。
后面会进行音频编解码以及蓝牙无线传输音频这两个部分的总结。