• Android : android 8.0 audio 接口分析


    1、HIDL 的概念

      HIDL 读作 hide-l,全称是 Hardware Interface Definition Language。它在 Android Project Treble 中被起草,在 Android 8.0 中被全面使用,其诞生目的是使 Android 可以在不重新编译 HAL 的情况下对 Framework 进行 OTA 升级 
      使用 HIDL 描述的 HAL 描述文件替换旧的用头文件描述的 HAL 文件的过程称为 * HAL 的 binder 化(binderization)。所有运行 Android O 的设备都必须只支持 binder 化后的 HAL 模块。 
      已发布的 HIDL package包位于 Android 代码库的hardware/interfaces/vendor/<vendorName>目录下。使用 HDIL 描述的 HAL 接口存放在这些目录下的.hal文件中。比如我们可以在hardware/interfaces/audio/2.0/目录下找到部分 Audio HAL 描述文件,如下:

    Android.bp
    Android.mk
    IDevice.hal
    IDevicesFactory.hal
    IPrimaryDevice.hal
    IStream.hal
    IStreamIn.hal
    IStreamOutCallback.hal
    IStreamOut.hal
    types.hal

    另外在frameworks/av/media/下多了个文件夹  libaudiohal :

    Android.mk                DeviceHalLocal.h             DevicesFactoryHalLocal.h  EffectHalHidl.h             EffectsFactoryHalLocal.h  StreamHalLocal.h
    ConversionHelperHidl.cpp  DevicesFactoryHalHidl.cpp    EffectBufferHalHidl.cpp   EffectHalLocal.cpp          HalDeathHandlerHidl.cpp
    ConversionHelperHidl.h    DevicesFactoryHalHidl.h      EffectBufferHalHidl.h     EffectHalLocal.h            include
    DeviceHalHidl.cpp         DevicesFactoryHalHybrid.cpp  EffectBufferHalLocal.cpp  EffectsFactoryHalHidl.cpp   StreamHalHidl.cpp
    DeviceHalHidl.h           DevicesFactoryHalHybrid.h    EffectBufferHalLocal.h    EffectsFactoryHalHidl.h     StreamHalHidl.h
    DeviceHalLocal.cpp        DevicesFactoryHalLocal.cpp   EffectHalHidl.cpp         EffectsFactoryHalLocal.cpp  StreamHalLocal.cpp

    从文件名命名方式来看,一类是以Hidl结尾,一类是Local结尾,Local结尾的应该是兼容之前的方式,即谷歌在文档里描述的:

    HIDL 旨在用于进程间通信 (IPC)。进程之间的通信经过 Binder 化。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持):

      要将运行早期版本的 Android 的设备更新为使用 Android O,您可以将惯用的(和旧版)HAL 封装在一个新 HIDL 接口中,该接口将在绑定式模式和同进程(直通)模式提供 HAL。这种封装对于 HAL 和 Android 框架来说都是透明的。直通模式仅适用于 C++ 客户端和实现。运行早期版本的 Android 的设备没有用 Java 编写的 HAL,因此 Java HAL 自然而然经过 Binder 化。

    直通式标头文件:

      编译 .hal 文件时,除了用于 Binder 通信的标头之外,hidl-gen 还会生成一个额外的直通标头文件 BsFoo.h;此标头定义了会被执行 dlopen 操作的函数。由于直通式 HAL 在它们被调用的同一进程中运行,因此在大多数情况下,直通方法由直接函数调用(同一线程)来调用。oneway 方法在各自的线程中运行,因为它们不需要等待 HAL 来处理它们(这意味着,在直通模式下使用 oneway 方法的所有 HAL 对于线程必须是安全的)。

      如果有一个 IFoo.halBsFoo.h 会封装 HIDL 生成的方法,以提供额外的功能(例如使 oneway 事务在其他线程中运行)。该文件类似于 BpFoo.h,不过,所需函数是直接调用的,并未使用 Binder 传递调用 IPC。未来,HAL 的实现可能提供多种实现结果,例如 FooFast HAL 和 FooAccurate HAL。在这种情况下,系统会针对每个额外的实现结果创建一个文件(例如 PTFooFast.cpp 和 PTFooAccurate.cpp)。

    2、Audio Record 调用分析:

     (1) Java层调用AndroidSDK中的API实例化一个AudioRecord对象

      -》 android-8.0.0_r4frameworksavmedialibaudioclientAudioRecord.cpp  -》  AudioRecord::AudioRecord

     (2) 设置相应参数 

      -》 mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
                        notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
                        uid, pid, pAttributes);

     (3)打开录音接口

         -》 // create the IAudioRecord
            status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);

     (4) 获取输入设备属性:

         status = AudioSystem::getInputForAttr(&mAttributes, &input,
                                            mSessionId,
                                            // FIXME compare to AudioTrack
                                            mClientPid,
                                            mClientUid,
                                            &config,
                                            mFlags, mSelectedDeviceId, &mPortId);

         其中是通过获取audio_policy_service建立binder接口:

         // establish binder interface to AudioPolicy service
         const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service() 

     (5) 调用AudioPolicyManager的接口 : android-8.0.0_r4frameworksavservicesaudiopolicymanagerdefaultAudioPolicyManager.cpp

       aps->getInputForAttr -》 AudioPolicyManager::getInputForAttr

     (6) 根据app传下的参数获取对应的设备类型:

      device = getDeviceAndMixForInputSource(inputSource, &policyMix); 

      -》Engine::getDeviceForInputSource      (android-8.0.0_r4frameworksavservicesaudiopolicyenginedefaultsrcEngine.cpp)

           一般录音软件是:AUDIO_SOURCE_MIC  ,google 语音引擎是:AUDIO_SOURCE_VOICE_RECOGNITION

       再根据当前系统支持的输入设备返回对应的录音设备:(默认的内置mic就是 AUDIO_DEVICE_IN_BUILTIN_MIC)

     if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO &&
                    availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
                device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
            } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
                device = AUDIO_DEVICE_IN_WIRED_HEADSET;
            } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
                device = AUDIO_DEVICE_IN_USB_HEADSET;
            } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
                device = AUDIO_DEVICE_IN_USB_DEVICE;
            } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
                device = AUDIO_DEVICE_IN_BUILTIN_MIC;
            }

    PS:通过 dumpsys media.audio_policy 指令查看当前系统所支持的设备模块及类型。

    可通过android-8.0.0_r4frameworksavservicesaudiopolicy目录里面 audio_policy.conf或者audio_policy_configuration.xml配置设备加载,

    使用.conf还是.xml取决于frameworks/av/services/audiopolicy/Android.mk 通过宏USE_XML_AUDIO_POLICY_CONF - 1 : 使用 .xml , 0: 使用 .conf。


      (7) 根据返回的device类型获取对应录音设备:

         *input = getInputForDevice(device, address, session, uid, inputSource,
                                     config->sample_rate, config->format, config->channel_mask, flags,
                                     policyMix);

     (8) 调用getInputProfile函数根据传进来的声音采样率、声音格式、通道掩码等参数与获得的设备支持的Input Profile比较返回一个与设备Profile匹配的IOProfile-》

         profile = getInputProfile(device, address,
                                          profileSamplingRate, profileFormat, profileChannelMask,
                                          profileFlags);

     (9) 根据返回的profile调用初始化时加载好的client接口:

    status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
                                                       &input,
                                                       &config,
                                                       &device,
                                                       address,
                                                       halInputSource,
                                                       profileFlags);

    PS: mpClientInterface是由AudioPolicyService中加载对应模块传递过来:

    void AudioPolicyService::onFirstRef()
    {
        {
            Mutex::Autolock _l(mLock);
    
            // start tone playback thread
            mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
            // start audio commands thread
            mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
            // start output activity command thread
            mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
    
            mAudioPolicyClient = new AudioPolicyClient(this);
            mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient); //对应的模块加载放在AudioPolicyManager的构造函数中   
        }
        // load audio processing modules
        sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
        {
            Mutex::Autolock _l(mLock);
            mAudioPolicyEffects = audioPolicyEffects;
        }
    }

     AudioPolicyManager构造函数中根据宏定义判断通过audio_policy.conf还是audio_policy_configuration.xml解析加载对应的so:

    #ifdef USE_XML_AUDIO_POLICY_CONF
        mVolumeCurves = new VolumeCurvesCollection();
        AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices,
                                 mDefaultOutputDevice, speakerDrcEnabled,
                                 static_cast<VolumeCurvesCollection *>(mVolumeCurves));
        if (deserializeAudioPolicyXmlConfig(config) != NO_ERROR) {
    #else
        mVolumeCurves = new StreamDescriptorCollection();
        AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices,
                                 mDefaultOutputDevice, speakerDrcEnabled);
        if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, config) != NO_ERROR) &&
                (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, config) != NO_ERROR)) {
    #endif

     加载模块:

            mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->getName());
            if (mHwModules[i]->mHandle == 0) {
                ALOGW("could not open HW module %s", mHwModules[i]->getName());
                continue;
            }

    具体加载过程:

    AudioFlinger::loadHwModule_l  

    -》mDevicesFactoryHal->openDevice(name, &dev);

      -》DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) 

        -》DevicesFactory::openDevice(IDevicesFactory::Device device, openDevice_cb _hidl_cb) //对应hardware/interfaces/audio/2.0/目录下IDevicesFactory.hal所描述接口

          -》loadAudioInterface(moduleName, &halDevice);

            -》DevicesFactory::loadAudioInterface(const char *if_name, audio_hw_device_t **dev)

       //加载so,后面最终通过dlopen加载了/system/lib/hw/下对应的so。
      rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod); if (rc) { ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); goto out; }
       //实际调用到了audio_hw.c中adev_open(),只会被调用一次,也就是给硬件模块中的函数指针赋值open()。 rc
    = audio_hw_device_open(mod, dev); if (rc) { ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); goto out; } if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) { ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version); rc = -EINVAL; audio_hw_device_close(*dev); goto out; }

     

     

     (10) 再回到AudioPolicyManager::getInputForDevice 中的 mpClientInterface->openInput调用流程:

        AudioFlinger::openInput -》 

          AudioFlinger::openInput_l -》

              sp<DeviceHalInterface> inHwHal = inHwDev->hwDevice();

               ...

                inHwHal->openInputStream -》

                DeviceHalHidl::openInputStream

     Return<void> ret = mDevice->openInputStream(
                handle,
                hidlDevice,
                hidlConfig,
                AudioInputFlag(flags),
                AudioSource(source),
                [&](Result r, const sp<IStreamIn>& result, const AudioConfig& suggestedConfig) {
                    retval = r;
                    if (retval == Result::OK) {
                        *inStream = new StreamInHalHidl(result); //audio_hw.c中adev_open_input_stream的参数stream_in在这里创建并传入
                    }
                    HidlUtils::audioConfigToHal(suggestedConfig, config);
                });
        return processReturn("openInputStream", ret, retval);

    -》最终调用到了audio_hw.c中的:

    static int adev_open_input_stream(struct audio_hw_device *dev,
                                      audio_io_handle_t handle,
                                      audio_devices_t devices,
                                      struct audio_config *config,
                                      struct audio_stream_in **stream_in,
                                      audio_input_flags_t flags,
                                      const char *address __unused,
                                      audio_source_t source )

    adev_open_input_stream中对sp<StreamInHalInterface> *inStream指针的各成员进行赋值,进一步调用底层的接口获取音频数据。

    PS:默认使用上层传下来的config参数,也可以手动更新stream_in的参数,比如 采样率:stream_in->config.rate,自己实现数据获取接口:stream_in->stream.read = my_read 等;


    -end-

  • 相关阅读:
    集成学习方法
    nlp知识
    kaggle竞赛入门整理
    pandas知识点
    EM算法
    特征工程
    【clustering】之K-means && K-medoids
    sklearn学习笔记
    周总结十二
    《如何高效学习》读书笔记五
  • 原文地址:https://www.cnblogs.com/blogs-of-lxl/p/8656286.html
Copyright © 2020-2023  润新知