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_r4\frameworks\av\media\libaudioclient\AudioRecord.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_r4\frameworks\av\services\audiopolicy\managerdefault\AudioPolicyManager.cpp

   aps->getInputForAttr -》 AudioPolicyManager::getInputForAttr

 (6) 根據app傳下的參數獲取對應的設備類型:

  device = getDeviceAndMixForInputSource(inputSource, &policyMix); 

  -》Engine::getDeviceForInputSource      (\android-8.0.0_r4\frameworks\av\services\audiopolicy\enginedefault\src\Engine.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_r4\frameworks\av\services\audiopolicy目錄里面 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-

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM