zoukankan      html  css  js  c++  java
  • 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进行操作的.

  • 相关阅读:
    02.ZooKeeper的Java客户端使用
    01.ZooKeeper安装和介绍
    02.Elasticsearch入门
    01.Elasticsearch安装
    01.ActiveMQ安装部署
    springboot项目打包时提示“程序包xxx不存在,找不到符号”
    Eclipse提交git代码 报错authentication not supported
    Eclipse提交git代码 报错authentication not supported
    utf8mb4_general_ci报错解决方案
    mysql开启远程访问
  • 原文地址:https://www.cnblogs.com/ouyshy/p/13452668.html
Copyright © 2011-2022 走看看