文档中心 > 芯片厂商开放

文档内容拷贝自:codebase_host/yunhal/opendoc/zh/yunhal_doc/audio.md
联系人:@钟学书

1. 概述(Introduction)

从AudioStreamer(YunOS Audio服务)看来,所有平台/厂商Audio相关部分都封装在具有清晰接口定义的Audio HAL层(硬件抽象层)。AudioStreamer的厂商相关部分分三个部分包括AuidoIOEngine、RouteEngine、DeviceMonitor,它们分别处理Auido IO操作、路由策略和设备连接监听。在Audio HAL的设计中使用面向对象的设计模型,其中包括对应上面三个部分的类,这三个HAL类对象都通过入口类AudioHwEngine来封装创建。

  • AudioIODevice: 它为典型的audio硬件设备比如:A2DP、HDMI、耳机等,定义通用的属性和操作。其中音频数据操作封装在基类DataChannel,输入输出部分分别从其继承为InputDataChannel和OutputDataChannel.
  • AudioRouteStrategyManager: 它定义通用的Audio路由方式,并可满足由厂商进行充分灵活的定制需求。
  • Audio DeviceMonitor: 它定义Audio可拔插设备的拔插事件通知,AudioStreamer可通过它的回调接口实现监听维护Audio设备的状态。

2. 架构(Audio Framework Architecture)

图1.1对Audio HAL进行了概览描述,每个HAL基类声明含有虚拟方法,具体实现由厂商进行实现。下面将分别具体介绍这几部分内容:

AudioYunHAL Overview

图1.1 YunHAL Audio模型

3. 实现(Implement Audio HAL)

3.1. AudioHwEngine

AudioHwEngine像如下代码一样声明成虚拟类。通过AudioHwEngine,AudioStreamer可查询HAL接口实现版本,并可创建其他对象比如IO设备、路由管理以及注册设备监听回调。

class AudioHwEngine {
    public:
        /**
        *   @brief return version of audio hardware engine.
        *   @since 4.0
        *   @return @c positive value for audio hal version
        *   @retval #HAL_AM_VER_1 for audio HAL 1
        *   @retval #HAL_AM_VER_2 for audio HAL 2
        *   @retval #HAL_AM_VER_3 for audio HAL 3
        */
        virtual am_version_t GetVersion() = 0;
        /**
        *   @brief create AudioIODevice according to the device type 
        *   @since 4.0
        *   @param[in] adev audio device type
        *   @return @c non-NULL if success, otherwise NULL
        *   @retval NULL for failure
        *   @retval non-NULL for success
        */
        virtual AudioIODevice* CreateIODevice(adev_type_t adev) = 0;
        /**
        *   @brief create AudioRouteStrategyManager with AudioRouteStrategyCallback 
        *   @since 4.0
        *   @param[in] cb instance of AudioRouteStrategyCallback
        *   @return @c non-NULL if success, otherwise NULL
        *   @retval NULL for failure
        *   @retval non-NULL for success
        */
        virtual AudioRouteStrategyManager* CreateAudioRouteStrategyManager(AudioRouteStrategyCallback *cb) = 0;
        /**
        *   @brief register AudioDeviceMonitor instance
        *   @since 4.0
        *   @param[in] cb object of AudioDeviceMonitorCallback
        */
        virtual void RegisterAudioDeviceMonitor(AudioDeviceMonitorCallback *callback) = 0;
        /**
        *   @brief destructor of AudioHwEngine
        *   @since 4.0.
        */
        virtual ~AudioHwEngine() {}
    };
    extern "C" AudioHwEngine* CreateAudioHwEngine();
    extern "C" void DestroyAudioHwEngine(AudioHwEngine* hwEngine);
    extern "C" AudioHwEngine* GetAudioHwEngine();
````

厂商仅仅需要实现上面虚拟HAL接口,就像如下示例代码:
AudioHwEngineVendor: public AudioHwEngine {
    // implements the AudioHwEngine interfaces
};

````

同时厂商需要在libauidohal库中实现CreateAudioHwEngine和DestroyAudioHwEngine接口,其实现往往类似如下代码:

AudioHwEngine* CreateAudioHwEngine() {
        return new AudioHwEngineVendor();
    }
    void DestroyAudioHwEngine(AudioHwEngine* hwEngine) {
        if (hwEngine == NULL) {
            return;
        }
        delete hwEngine;
    }

在运行时,AudioStreamer从/usr/lib/yunhal目录动态装载libaudiohal库,并解析CreateAudioHwEngine和DestroyAudioHwEngine符号,一旦AudioHwEngine对象创建成功,就可开始使用Audio HAL。

3.1.1. Audio IO Device

从AudioStreamer看来,AudioIODevice对所有Audio物理设备的操作进行抽象,对于IO操作,AudioIODevice根据数据的来源和接受不同分开进行数据流程处理。
就像下图2.1所示,Audio数据流被抽象成DataChannel基类,输出和输入分别继承为OutputDataChannel和InputDataChannel,Audio数据的来源端和接受端被抽象成EndPoint.

AudioIODevice Terminology Overview

图2.1 Audio Streamer模型

从DataChannel中分离出EndPoint的主要动机在于:Audio设备可能有不同的物理属性,但它们共享通用的数据操作模式。这样AudioIODevice可以路由同样的DataChannel到不同的Audio物理设备EndPoint。这种可在多个Audio EndPoint之间路由DataChannel称隧道模式。比如:当用户在通过扬声器听音乐时插入耳机,音乐将通过路由到音机输出,其中不会包含其他复杂的操作流程。
除了Audio输入输出,HAL也可以为AudioStreamer提供硬件A/V同步机制,以便利用硬件资源的高效使用,而这正好是AudioStreamer设计上的灵活性,下面分别介绍各个术语:

3.1.2. EndPoint

EndPoint术语代表生产或消费Audio数据路径中终点。EndPoint可以是真实的Audio物理设备,也可以试逻辑上的Audio数据生产者或消费者。物理设备的例子有扬声器、耳机、收音机。在AudioIOEngine中逻辑上的EndPoint有Audio混合器/重采样/渲染器等。一个典型的Audio播放pipeline往往像图3所示由多个Audio EndPoint组成。

Audio Playback Pipeline

图2.2 Audio播放pipeline

Audio数据在数据流转路径中流动,可能会经过多个物理EndPoint和逻辑EndPoint。虽然这些EndPoint对最终的Audio物理设备讲是非常重要的,但它们往往对用户不可见,从AudioStreamer看来,一组链接好的Audio EndPoint可构成另一个EndPoint。比如:在Audio路由管理器看来,播放管道可以认为成一个Audio EndPoint,而Audio路由管理器在Audio数据流和它的消费者之间执行路由选择。

3.1.3. Tunneling

Audio通道可以通过Audio路由管理器或由应用使用框架接口进行链接和释放链接,Audio通道可以是物理EndPoint和逻辑EndPoint之间建立的链接,或者是逻辑EndPoint之间建立的链接,甚至可以是物理EndPoint之间建立的链接。比如:AudioStreamer可以调用AudioIODevice HAL接口在收音机和耳机之间建立通道,在这种方式中,由Fm收音机产生的Audio数据可以不经过AudioStreamer参与而路由到耳机。
Audio Routing & Tunneling

图2.3 Audio Tunneling

每一个通道由创建这个通道时传递的句柄来标识,当需要创建一个通道时,AudioIODevice将分配并返回一个通道句柄,但这个句柄在给定的 AudioIODevice中是唯一的,为了区分由不同AudioIODevice对象创建的通道句柄,具有相同通道句柄的通道将接受另一个由AudioStreamer框架分配的系统唯一的句柄,这个唯一的句柄可用在所有Audio框架的事务中。

3.1.4. Data Channel

DataChannel是指Audio数据流和其相关操作的逻辑描述,作为具有独立的数据流管理机制的EndPoint,DataChannel由下面基础功能:

  • Attributes and Configuration: 首先,DataChannel有一些平台相关的属性比如缓冲区大小、采样率、Audio格式和这个DataChannel可连接的EndPoint. 为了获取/设置这些内部属性,需要相应获取/设置接口。

  • Sound Effect Hooking: DataChannel属于Audio处理管道,在框架设计上,AudioStreamer也容许应用指定在DataChannel内由Audio HAL调用的音效算法模块,这些音效算法可以由框架或厂商自己提供。

  • Power State Control: AudioStreamer把Audio IO HAL当成一个运行环境,它暴露跟电源状态控制相关接口给AudioStreamer。一旦没有任何由AudioStreamer触发的运行时任务,Audio IO HAL进入待机模式,同时当有下一个IO操作发生时,Audio IO HAL将从待机模式退出。

基于DataChannel,继承出InputDataChannel和OutputDataChannel来处理输入和输出。
在剩下的子章节中,将分别介绍InputDataChannel和OutputDataChannel。

3.1.5. Input Data Channel

根据框架提供的Audio设置比如通道mask、数据格式和frame数量,由AudioIODevice对象来创建它,根据这些Audio设置,HAL可以为框架计算出相应的Audio缓冲区大小。其中最主要接口是读,它是阻塞式的操作。为了分摊传输过程中Audio信号的丢失,也容许厂商为了放大Audio信号而设置信号增益。一般说来,由InputDataChannel来缓存Audio数据。如果用户空间框架不能尽可能来读取Audio数据,Audio缓冲数据可以超出Audio HAL的能力,这样会导致缓冲区溢出。这种情形是有可能发生的,特别是Audio框架被阻塞。为了处理这种情形,它定义了一个HAL接口,可用来查询自从上次调用这个接口以来Audio输入frame的丢失数量。这样,AudioStreamer可以有机会用零来填充这些丢失的帧。

3.1.6. Output Data Channel

OutputDataChannel提供Audio输出操作的逻辑抽象,就像下图5所示,它主要包含三种数据操作:

  • Write: 一般说来,OutputDataChannel包含一组数据缓冲队列,写操作是指把框架内部缓冲数据填充到OutputDataChannel的数据缓冲队列,返回成功写入的字节数或错误码。除了是阻塞式的操作外,它也提供非阻塞式的写操作,AudioStreamer可注册数据回调到Audio HAL.

  • Drain: 这个操作是指把数据缓冲队列中的数据输出到EndPoint设备中。

  • Flush: 它是指丢弃数据缓冲队列中的数据。

OutputDataChannel

图2.4 Audio output 数据通道

除了上面提到的Write/Drain/Flush数据操作,OutputDataChannel也提供接口操作它的内部状态机,如下图6所示,内部状态机由三种状态组成:运行中、暂停、待机。有相应的操作执行状态的转换比如暂停/恢复和待机。除了这些显示的接口,也有内部转换,比如:由于超时导致从暂停转换到待机,一旦有任何IO事件导致从待机转换到运行。

OutputDataChannel State Machine

图2.5 Audio状态机

另外一个OutputChannel支持的功能是硬件offloading。它能offload一些Audio任务比如编解码、音效加速到指定的硬件比如编解码芯片,从而减少功耗。如下图7所示,整个Audio的输出管道可分成三个阶段:write、render和present。将Audio缓冲队列数据写到D/A中的过程叫做render,而Audio被人们的耳朵感知到的过程叫做present。OutputDataChannel自身包含一个可容纳成百上千个Audio采样的缓冲区,write操作不意味着Audio数据在Audio诸如扬声器或扩音器中播放,Audio数据可以缓存在Audio缓冲队列中。为了协调AudioStreamer和Audio HAL的交互,在OutputDataChannel定义了新接口和属性。

  • Latency: 它是指需要完成Audio render的时间,它对AudioStreamer评估在获取下一批Audio数据前可以睡眠多久。

  • AccumulatedRenderSamplesSinceLastStandby: 它是指自从最后一次的待机模式后累计render的Audio样本数。这个属性对使用硬件offloading实现同步A/V非常有用,它是AudioStreamer可以获取render过的Audio采样数的唯一方式,比如:为了知道多少Audio样本已经render。

  • NextSamplePTS: 它是指为了下一次写Audio数据而评估出来的PTS,它可使用公式LocalTime+(QueueBufferSize + NextSampleSize)/DeviceSampleRate来计算。它往往用于A/V同步,以预测后续Audio Write的PTS。

  • RecentSamplePTS: 它是指最近render过的Audio样本PTS,也用于计算在这个DataChannel的时间戳。

OutputDataChannel Pipeline

图2.6 Audio offloading

总的说来,伴随着硬件offloading,Audio HAL实现需要尽最大的努力提供足够的信息去评估PTS和Audio frame数量。我们称这种PTS和Audio frame数为ATS.
ATS用于协调在Audio处理过程中事件和交互,就像Audio播放器和视频播放器中的A/V同步。

3.1.7. Audio IO Device 总结

在解释完Audio IO HAL相关术语包括设备、EndPoint、Tunneling、DataChannel、属性参数设置后,我们用一张大图进行汇总,AudioHwEngine、AudioIODevice、DataChannel、InputDataChannel、OutputDataChannel和NonBlockingWriteCallback之间的继承和交互图描述如下图8所示。HAL对象AudioHwEngine通过C接口CreateAudioHwEngine进行创建,后来的HAL AudioIODevice和DataChannel对象通过AudioHwEngine来创建。当需要使用非阻塞式write时,NonBlockingWriteCallback由AudioStreamer来实现并注册到OutputDataChannel.在一个AudioIODevice上可以有多个EndPoints,也可以在这些EndPoints直接建立通道,那些高级特性比如:A/V同步、硬件offloading,通过参数属性系统来实现。

AudioIODevice HAL Interface

图2.7 Audio 类继承关系

3.2. AudioRouteStrategyManager

在Audio系统中,为了用户体验需要管理不同的设备成为较大挑战之一,需要同时接管多个设备是Audio框架和其他系统框架不同之处的表现之一,另外设备的状态变化可能随时发生。比如:用户可在任何时间插入耳机,连接蓝牙耳机或调整音量,在这样的条件下,Audio框架需要足够的智能以应对切换/路由到正确的设备或及时调整对应类型的音量。Audio框架中的AudioRouteStrategyManager(简称ARS)负责在这种条件下达到好的用户体验,它作为路由引擎与AudioStreamer的其他部分一起协作,负担起如下角色:

  • Maintain the audio system state: ARS最基础的功能是维护设备的链接状态,一旦设备连接或失去连接,ARS必须即时更新它的内部状态,同时,ARS也需要维护电话状态,以及系统当前处在何种模式,以及用户请求强制使用的功能状态等。

  • Take charge of the volume adjustment: 音量可以简单的当成Audio EndPoin的index值,然而index值必须通过ARS转换成浮点值,而后使用浮点值作用在目标播放设备上。音量的转换设计成基于相应stream类型的与平台相关的曲线。ARS使用这个曲线去映射index值对应的浮点值。ARS同时需要维持设备有无静音的状态。

  • Conduct the routing process among endpoints: ARS的主要任务在EndPoint之间切换/路由事务,当一个AudioRender/AudioCapture对象创建,开始播放/录制一个声音时,一个EndPoint句柄需要通过其中的GetEndPoint接口得到。ARS将根据当前是否已存在相应配置满足这个请求,否则将新创建一个,同时根据当前系统状态路由到哪个Audio设备。

3.2.1. Route Configuration

在ARS初始化阶段,ARS需要从系统路径装载平台指定配置文件,然后根据其内容维护系统状态,比如:为内建的主EndPoint支持的采样率建立句柄;根据当前已连接的设备设置缺省路由。在这个配置文件中,有两部分必须给出清晰定义:

1). The global and default device information: 在这部分,须告诉ARS哪些类型的输入/输出设备是一直存在(或处于连接状态),以及哪个输出设备是缺省选择。有了这些信息,可用的输入/输出设备状态才可初始完成,这是后续进行设备路由选择的基础,下面是针对这部分的示例配置:

global_configuration {
        attached_output_devices ADEV_ENDPOINT_OUT_EARPIECE|ADEV_ENDPOINT_OUT_SPEAKER
        default_output_device ADEV_ENDPOINT_OUT_SPEAKER
        attached_input_devices ADEV_ENDPOINT_IN_BUILTIN_MIC| ADEV_ENDPOINT_IN_REMOTE_SUBMIX
    }

2). The detail descriptions on all supported devices: 在这部分,它作为硬件模块来描述,每个模块有不同的名字,所有支持的设备必须在inputs/outputs中列出。每个模块的能力须更加详细的描述包括支持的采样率、channel数量、格式等,这样在进行路由处理时ARS可以了解全局的状态,并根据期望的配置信息选择正确的设备,因此,为了作出更加智能的决定,这些对ARS来说是必不可少的一部分。下面是一个配置示例供参考:

audio_hw_modules {
        primary {
            outputs {
                primary {
                    sampling_rates 44100
                    channel_masks ADEV_CHANNEL_OUT_STEREO
                    formats SND_FORMAT_PCM_16_BIT
                    devices ADEV_ENDPOINT_OUT_EARPIECE|ADEV_ENDPOINT_OUT_SPEAKER|… …
                    flags ADEV_OUTPUT_FLAG_PRIMARY
               }
           }
           inputs {
               primary {
                   sampling_rates 8000|11025|16000|22050|24000|32000|44100|48000
                   channel_masks ADEV_CHANNEL_IN_MONO|ADEV_CHANNEL_IN_STEREO
                   formats SND_FORMAT_PCM_16_BIT
                   devices ADEV_ENDPOINT_IN_BUILTIN_MIC|ADEV_ENDPOINT_IN_WIRED_HEADSET|… …
               }
           }
           a2dp {
               outputs {
                   a2dp {
                       sampling_rates 44100
                       channel_masks ADEV_CHANNEL_OUT_STEREO
                       formats SND_FORMAT_PCM_16_BIT
                       devices ADEV_ENDPOINT_OUT_ALL_A2DP
                   }
               }
           }
        … …
       }

3.2.2. ARS HAL

这部分更加详细描述ARS接口,如下图9所示,ARS往往与AudioStreamer的RouteEngine进行交互,当RouteEngine发起一个路由请求时,ARS进行相应处理。在ARS与RouteEngine之间,有两种类型的接口来负责它们之间的交互任务比如:AudioRouteStrategyManager和AudioRouteStrategyCallback。

AudioRouteStrategyManager接口定义ARS本身的接口,RouteEngine通过调用这些接口向ARS发起路由请求,AudioRouteStrategyManager接口可分为三类:第一类是维护系统状态包括SetConfig、GetConfig和IsStreamActive。所有的系统状态都能够通过这些接口获取/设置;第二类是控制音量的改变,SetVolumeControl和GetVolumeControl;最后一部分在EndPoint之间进行路由处理,整个EndPonit的生命周期都由这类接口来说控制。
RouteEngine实现AudioRouteStrategyCallback回调接口,它将由ARS在进行路由处理时进行调用,它也相应分为三类。更加详细的描述详见图9。

ARS HAL Interface

图2.8 Audio ARS接口

3.2.3. Audio system state

ARS的基本职责在于跟踪维护系统状态的改变,ARS可以根据当前的系统状态作出路由决定,ARS需要维护多种系统状态,任何的系统状态都可影响路由处理,下面是讲系统状态分成如下四类:

1). Device Connection State
就像前面提到的,Audio框架同一时刻必须管理不同的Audio设备,从ARS的角度看,这些设备可分为两类:已连接上的设备和可移除的设备。
ARS把已连接上的设备当成可路由到达的设备,在ARD初始阶段,ARS从平台配置文件中获知这些设备的信息,同时设置它们是可用状态。在大多数情况下,这些设备的连接状态是不可变的。ARS会在初始化时基于这些设备模块创建EndPoint,同时不会关闭它。这种设备的类型包括扬声器等。
可移除的设备是指用户可以拨插的任何设备,ARS通过接受到通知更新事件,维护其状态,并将当前流切换到其他设备。如图3展示改变设备连接状态的工作流程,当DeviceMonitor发现有新设备连接上,RouteEngine将调用ARS的SetConfig接口以改变其状态。如果这个设备的相关模块以前没有装载,ARS必须先装载它,然后在内部保存这个设备新的状态,同时通知AudioIOEngine这些改变。在这以后,ARS必须检查当前是否有正在处理的流,并根据当前的系统状态和路由方式决定是否需要切换到这个设备,如果需要切换,则通过发送路由改变请求的通知。

2). Phone state
设备连接状态描述哪些类型设备是可用的,而电话状态用来记录当前手机处在哪种模式。例如:当一个电话正在拨入,铃声正在播放,同时手机设置成RINGTONE模式。另外一个例子是在VoIP建立通话期间,手机的模式是COMMUNICATION。一旦建立好通话连接,手机处于IN_CALL模式。
这些模式对路由处理的结果和其他设备状态改变产生的结果都直接产生影响,如果手机处于RINGTONE模式,播放音乐的流应该立即自动终止。类似的,当手机处于IN_CALL模式,任何其他的通知都当成tone音路由到当前设备。为了实现这些场景的处理,ARS须维护当前电话状态,同时提供接口可改变电话状态,例如Telephony须调用SetPhoneState接口来通知ARS来改变电话状态。

3). Force use
为了满足千变万化的用户场景,ARS提供force use功能,它容许用户影响路由决定。这些场景往往发生在真实的使用场景,例如:当正在播电话,电话状态已设置成IN_CALL模式,ARS缺省路由Audio输出到耳机,这种结果在大多数情况下是可以接受和期望的,但是有时用户期望通过扬声器来进行通话。在这种场景下,需要由Telephony调用ARS SetConfig接口请求路由到扬声器,剩下的任务由ARS来将当前通话从耳机切换到扬声器,这个force use功能为用户提供更加灵活的路由决策。

4). Stream state
stream状态不能代表ARS自身内部的状态,ARS根据RouteEngine的请求打开多个EndPoints,Audio stream在不同的用户场景对应的EndPoint之间路由,在设计上,仅仅只有几种类型的输出stream可以同时播放,为了获取当前哪种类型stream正在播放,ARS可以查询这种状态。
有时系统进程也需要查询这种状态,比如:SystemUI和Launcher,这些进程想知道在最近的时间段内活动的stream类型,然后它们可告知点击按键可以调整对应类型的stream的音量。在这种情况下,ARS IsStreamActive接口可以用来获取指定类型的stream是否在播放。

3.3. Audio Device Monitor

在Audio系统中,Audio框架须同时接管多个设备,而设备的状态可能随时改变。比如:用户可以插入耳机,连接蓝牙耳机或调节音量。在这些场景中,Audio框架需要获得状态改变通知,并且让AudioRouteStrategyManager有机会去切换到正确的设备和正确调整的音量,Audio DeviceMonitor是用来负责Audio框架可获得通知的组件,获得通知接口逻辑如下图10所示:

Audio DM HAL Interface

图2.8 Audio Device Monitor

4. 测试(Vendor Test Suite)

5. 附录(Appendix)

FAQ

关于此文档暂时还没有FAQ
返回
顶部