Android Audio 架構自學筆記(三) audio Flinger 基本概述


通過前兩次對android audio整體架構的解析,我們已經基本了解andriod audio框架的基本組成以及android audio hal對上層所提供的基本接口。

由android audio架構中了解,android audio framework中的audioFlinger是andriod audio hal的直接使用者,那么接下來我們就看一下android audioFlinger是如何使用Android audio hal來實現audio基本功能的.

android audioFlinger 服務代碼結構大致如下(圖中只表現出來一部分);

 

 

 通過圖中文件名稱再結合audio hal功能基本解析的結果,我們就可以大概了解其文件內容;

(1)AudioFlinger為audio Flinger服務的入口,其中應該能夠包括audioFlinger對外提供的服務;

(2)AudioHwDevice為audio hal的上層抽象,通過名稱我們可以猜測這個文件內容中操作大概與audio hal 的操作相對應;

(3)AudioStreamOut為audio hal outputStream的上層抽象。這里面的內容應該就是與audio hal stream_out的操作相對應。

(4)其他,暫時還沒什么頭緒。

按照以前的分析方法,我們仍然先去調查audioFlinger對外提供了哪些能力。然后再又上到下的調查方法查看這些能力是怎樣與底層hal相結合來進行實現的。

audioFlinger文件聲明的主要方法如下:

virtual sp<IAudioTrack> createTrack(參數先略)

virtual sp<IAudioRecord> openRecord(
                   audio_io_handle_t input,
                   uint32_t sampleRate,
                   audio_format_t format,
                   audio_channel_mask_t channelMask,
                  const String16& opPackageName,
                   size_t *pFrameCount,
                   audio_input_flags_t *flags,
                   pid_t pid,
                   pid_t tid,
                   int clientUid,
                   audio_session_t *sessionId,
                   size_t *notificationFrames,
                   sp<IMemory>& cblk,
                   sp<IMemory>& buffers,
                   status_t *status /*non-NULL*/,
                   audio_port_handle_t portId);

virtual status_t openOutput(audio_module_handle_t module,
             audio_io_handle_t *output,
             audio_config_t *config,
             audio_devices_t *devices,
             const String8& address,
             uint32_t *latencyMs,
             audio_output_flags_t flags);

virtual status_t openInput(audio_module_handle_t module,
             audio_io_handle_t *input,
             audio_config_t *config,
             audio_devices_t *device,
             const String8& address,
             audio_source_t source,
             audio_input_flags_t flags);

virtual audio_module_handle_t loadHwModule(const char *name);

.......

我們先將這些函數摘出來的主要原因是這些函數剛好與我們在hal 層分析的上層操作基本時序相對應,即先加載動態庫(loadHwModule),然后根據參數使用方法創建輸入輸出流(openOutput、openInput),然后再通過流來進行數據操作。

我們先梳理整體時序,然后在了解整體時序的基礎之上再對內部細節進行詳細了解,否則很容易只關注了細節而忽略了對軟件整體設計的理解。

1、virtual audio_module_handle_t loadHwModule(const char *name);通過名稱來加載hal

在源碼中,我們可以清楚看到loadHwModule對輸入參數name合法性檢查以及某個系統檢查之后就直接調用loadHwModule_l來進行具體功能實現了;

audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{
   for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
   if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) {
   ALOGW("loadHwModule() module %s already loaded", name);
   return mAudioHwDevs.keyAt(i);
   }
   }
  
   sp<DeviceHalInterface> dev;
  
   int rc = mDevicesFactoryHal->openDevice(name, &dev);
   if (rc) {
  ALOGE("loadHwModule() error %d loading module %s", rc, name);
   return AUDIO_MODULE_HANDLE_NONE;
   }
  
   mHardwareStatus = AUDIO_HW_INIT;
   rc = dev->initCheck();
   mHardwareStatus = AUDIO_HW_IDLE;
   if (rc) {
   ALOGE("loadHwModule() init check error %d for module %s", rc, name);
   return AUDIO_MODULE_HANDLE_NONE;
   }
  
   // Check and cache this HAL's level of support for master mute and master
  // volume. If this is the first HAL opened, and it supports the get
   // methods, use the initial values provided by the HAL as the current
   // master mute and volume settings.
  
   AudioHwDevice::Flags flags = static_cast<AudioHwDevice::Flags>(0);
   { // scope for auto-lock pattern
   AutoMutex lock(mHardwareLock);
  
   if (0 == mAudioHwDevs.size()) {
   mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
   float mv;
   if (OK == dev->getMasterVolume(&mv)) {
   mMasterVolume = mv;
   }
  
   mHardwareStatus = AUDIO_HW_GET_MASTER_MUTE;
   bool mm;
   if (OK == dev->getMasterMute(&mm)) {
   mMasterMute = mm;
   }
   }
  
   mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
   if (OK == dev->setMasterVolume(mMasterVolume)) {
   flags = static_cast<AudioHwDevice::Flags>(flags |
   AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME);
   }
  
   mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
   if (OK == dev->setMasterMute(mMasterMute)) {
   flags = static_cast<AudioHwDevice::Flags>(flags |
   AudioHwDevice::AHWD_CAN_SET_MASTER_MUTE);
   }
  
   mHardwareStatus = AUDIO_HW_IDLE;
   }
  
   audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);
   mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
  
   ALOGI("loadHwModule() Loaded %s audio interface, handle %d", name, handle);
  
   return handle;
}

我們對這個函數的基本流程進行梳理:

(1)首先判斷輸入的參數name是否在某個集合中存在,如果存在則直接返回hanle

(2)如果不存則使用halfactory對象通過名稱打開(加載)相應的資源,並返回底層資源對象(dev)

(3)然后通過底層對象dev,先檢查資源初始化狀態。如果初始化狀態符合操作條件則進行下一步操作,否則直接返回錯誤狀態

(4)設置底層設備的音量、mute等屬性

(5)生成唯一handle標識,創建上層AudioHwDevice對象與底層資源進行對應。然后將AudioHwDevice對象假如到mAudioHwDevs集合中,並返回對象唯一handle標識

通過上述流程梳理,我們可以了解到如下內容

(1)AudioHwDevice為底層hal的上層抽象,一個AudioHwDevice就對應着一個hal.

(2)我們可以通過AudioHwDevice獲取底層資源dev,來對底層資源操作

(3)dev與handle進行綁定。我們可以通過handle在mAudioHwDevs集合中獲取AudioHwDevice,然后再獲得dev.或者通過name直接獲得handle,然后再獲得AudioHwDevice進而獲得dev

2、virtual status_t openOutputaudio_module_handle_t module...)

在源碼中,我們可以清楚看到openOutput對輸入參數合法性檢查以及某個系統檢查之后就直接調用openOutput_l來進行具體功能實現了;

  sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(audio_module_handle_t module,
                             audio_io_handle_t *output,
                             audio_config_t *config,
                             audio_devices_t devices,
                             const String8& address,
                             audio_output_flags_t flags)
  {
   AudioHwDevice *outHwDev = findSuitableHwDev_l(module, devices);
   if (outHwDev == NULL) {
   return 0;
  }
  
  if (*output == AUDIO_IO_HANDLE_NONE) {
  *output = nextUniqueId(AUDIO_UNIQUE_ID_USE_OUTPUT);
   } else {
   // Audio Policy does not currently request a specific output handle.
   // If this is ever needed, see openInput_l() for example code.
   ALOGE("openOutput_l requested output handle %d is not AUDIO_IO_HANDLE_NONE", *output);
   return 0;
   }
  
   mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
  
   // FOR TESTING ONLY:
   // This if statement allows overriding the audio policy settings
   // and forcing a specific format or channel mask to the HAL/Sink device for testing.
   if (!(flags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT))) {
   // Check only for Normal Mixing mode
   if (kEnableExtendedPrecision) {
   // Specify format (uncomment one below to choose)
   //config->format = AUDIO_FORMAT_PCM_FLOAT;
   //config->format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
   //config->format = AUDIO_FORMAT_PCM_32_BIT;
   //config->format = AUDIO_FORMAT_PCM_8_24_BIT;
   // ALOGV("openOutput_l() upgrading format to %#08x", config->format);
   }
   if (kEnableExtendedChannels) {
   // Specify channel mask (uncomment one below to choose)
   //config->channel_mask = audio_channel_out_mask_from_count(4); // for USB 4ch
   //config->channel_mask = audio_channel_mask_from_representation_and_bits(
   // AUDIO_CHANNEL_REPRESENTATION_INDEX, (1 << 4) - 1); // another 4ch example
   }
   }
  
  AudioStreamOut *outputStream = NULL;
   status_t status = outHwDev->openOutputStream(
   &outputStream,
   *output,
   devices,
   flags,
   config,
   address.string());
  
   mHardwareStatus = AUDIO_HW_IDLE;
  
   if (status == NO_ERROR) {
   if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
   sp<MmapPlaybackThread> thread =
   new MmapPlaybackThread(this, *output, outHwDev, outputStream,
   devices, AUDIO_DEVICE_NONE, mSystemReady);
  mMmapThreads.add(*output, thread);
  ALOGV("openOutput_l() created mmap playback thread: ID %d thread %p",
  *output, thread.get());
  return thread;
  } else {
  sp<PlaybackThread> thread;
  if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
  thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);
  ALOGV("openOutput_l() created offload output: ID %d thread %p",
  *output, thread.get());
  } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)
  || !isValidPcmSinkFormat(config->format)
  || !isValidPcmSinkChannelMask(config->channel_mask)) {
  thread = new DirectOutputThread(this, outputStream, *output, devices, mSystemReady);
  ALOGV("openOutput_l() created direct output: ID %d thread %p",
  *output, thread.get());
  } else {
  thread = new MixerThread(this, outputStream, *output, devices, mSystemReady);
  ALOGV("openOutput_l() created mixer output: ID %d thread %p",
  *output, thread.get());
  }
  mPlaybackThreads.add(*output, thread);
  return thread;
  }
  }
  
  return 0;
 }

我們對這個函數的基本流程進行梳理:

(1)首先根據輸入的module以及devices找到底層所使用的hal資源

(2)生成唯一標識的output ID

(3)通過底層資源創建AudioStreamOut 輸出數據流

(4)創建一個線程與輸出數據流以及outputID進行綁定

(5)將創建的線程進行保存

通過上述流程梳理,我們可以了解到如下內容

(1)openOutput實際就是底層資源根據設備以及設備配置創建輸出數據流

(2)每一個數據數據流都與一個線程進行綁定,並以outputID作為唯一標識

那么根據以上內容,我們就可以了解了在audioFlinger對底層audio hal的基本操作模式。

也由此可以推斷出上層對數據流的相關操作大概時序為下:

1、先通過openoutput創建數據流

2、通過某種方式獲得數據流的唯一標識outputID

3、通過outputID找到數據流對應的線程

4、然后將要寫入到輸出流的音頻數據與線程建立聯系,最終將數據寫入到hal 層進而寫到實際物理設備之中

我們就先將audioFlinger梳理到這里,這次的主要目的就是了解audioflinger與底層hal層的交互的基本步驟,下一步就沿着這個基本步驟,去調查stream對應的線程是如何對stream進行操作的.


免責聲明!

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



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