Android 语音遥控器的整体分析-HAL层的AudioFlinger

上篇说到语音部分最后会通过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:

Android 语音遥控器的整体分析-HAL层的AudioFlinger_第1张图片

分析代码是上面的类关系,可以知道,在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层的系统工程师和驱动工程师配合工作,驱动工程师完成音频驱动后,给出音频数据的节点,Android系统层去对这个设备节点进行读写。或者与其他网络模块比如蓝牙模块工作的时候,可以不通过内核。遥控器端将数据加密,编码压缩后,通过蓝牙部分接受裸数据,然后蓝牙模块通过socket传给语音模块进行解码,然后给上层使用。


后面会进行音频编解码以及蓝牙无线传输音频这两个部分的总结。






你可能感兴趣的:(Android 语音遥控器的整体分析-HAL层的AudioFlinger)