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:


分析代码是上面的类关系,可以知道,在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传给语音模块进行解码,然后给上层使用。


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






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ble audio补丁原理是利用hidraw节点捕捉协议栈发送的语音数据,目前Android Blueroid将ble语音数据和按键信息通过hid发送出去,通过建立hidraw节点,可以从中捕捉到语音数据。目前通过ble hal实现从hidraw中读取遥控器语音数据,在Android框架上就通过配置文件将ble hal导入到音频框架中,并通过绑定Android原生已有的耳麦设备来完成audio音频策略选择,通过apk检测ble连接状态,通知audio服务耳麦设备的状态就可以使得录音通路切换至ble hal,实现从ble获取录音数据功能。 打补丁前最好使用干净的环境,不要有别家方案ble补丁,否则可能会有不兼容问题。 补丁如若不能使用首先检查节点是否存在和其权限,正常节点权限如下: ls -l /dev/hidraw* crw-rw---- 1 system audio 241, 0 2018-12-18 13:42 /dev/hidraw0 audio用户组有读写权限。 2、如果selinux模式为Enforcing,可以通过logcat搜索avc关键字。有如下类似提示则为异常,提示进程没有权限,检查sepolicy是否设置正常: avc: denied { read } for name="/" dev="tmpfs" ino=6145 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=0 //Android 5.0和6.0版本,audio hal被mediaserver进程加载 avc: denied { read } for name="/" dev="tmpfs" ino=8125 scontext=u:r:audioserver:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=0 //Android 7.0版本,audio halaudioserver进程加载 avc: denied { read } for name="hidraw" dev="sysfs" ino=16332 scontext=u:r:hal_audio_default:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0 //Android 8.0和9.0版本,audio halandroid.hardware.audio@2.0-service进程加载 3、检查audio的配置,打上patch后,首先确认小机上文件是否有修改到,目前文件可能位于/vendor/etc或/system/etc目录下,其中/vendor/etc下的配置文件是优先解析的。确保文件无误后,通过dumpsys media.audio_policy查看ble hal是否正常加载。 以下是相关说明: AudioPolicyManager: 0xf20c5200 Command Thread: 0xf20af140 Tones Thread: 0xf20af020 ... - Available input devices: Device 1: - id: 3 - type: AUDIO_DEVICE_IN_BUILTIN_MIC - Profiles: Profile 0: - format: AUDIO_FORMAT_PCM_16_BIT - sampling rates:8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 - channel masks:0x000c, 0x0010 Device 2: - id: 20 - type: AUDIO_DEVICE_IN_WIRED_HEADSET //对应的数值是0x80000010 - name: RemoteDM1204 - Profiles: Available input devices指示当前可用设备,目前ble hal是和AUDIO_DEVICE_IN_WIRED_HEADSET设备绑定,如果需要录音走ble halAUDIO_DEVICE_IN_WIRED_HEADSET设备必须出现在可用设备中,如果没有,就可能是补丁中hidaudio.apk的问题。 HW Modules dump: ... - H

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值