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进行操作的.

posted @ 2020-08-07 15:53  oy182104619  阅读(3935)  评论(1编辑  收藏  举报