日期:2014-05-20  浏览次数:21171 次

音频hal层总结+frameworks 概要

音频hal层总结+frameworks 概要

 

第一部分

三个文件,其中所有类,都为根类.文件地址:

1:Audio.h 有四个类

.audio_stream_out: 主要有四个成员函数: *get_latency  set_volume  write  get_render_position   并且包含: struct audio_stream common;

 

 . audio_stream_in:主要有三个成员函数: set_gain(调音)  read  get_input_frames_lost

   函数getInputFramesLost,调用checkRecordThread_l获取录音线程,然后调用线程的getInputFramesLost函数。

   并且包含: struct audio_stream common;

 

 . struct audio_module {   struct hw_module_t common; };   

 

. audio_hw_device 包含很多成员函数

 

 

2: audio_effect.h 音效文件先不表.

 

3: audio_policy.h 策略管理

①.    audio_policy:

 

②.    audio_policy_service_ops

   

  . typedef struct audio_policy_module { struct hw_module_t common;} audio_policy_module_t;

 

  . audio_policy_device:

    

 

 

第二部分: STUB(模拟测试)程序

   文件目录:  两个文件

1: audio_hw.c

2: audio_policy.c

   这两个程序,其实就是两个Stub(模拟测试)程序. 我们hal层可以参照来写.

 

 

 

第三部分:兼容2.3以前的程序 文件目录:

1: AudioSystemLegacy.h

namespace来看,确实4.0以后可能不用到. Using 使用名字空间表示以下代表在status_t AudioParameter空间可见..

 

2: AudioHardwareBase.h

 

3:AudioHardwareInterface.h

  有三个类, AudioStreamOut  AudioStreamIn   AudioHardwareInterface

 

 AudioStreamIn  成员函员包括以下

其中Read Write 函数是最关键的了.

 

4: AudioPolicyInterface.h

5: AudioPolicyManagerBase.h

上面两个都是音频软硬件策略文件了.

 

 

功能如: 可以看出包括软硬件的策略管理.

 

 

 

第三部分之二(兼容2.3之前代码) 文件路径:

从以下文件名可以知道程序功能:

以前的主程序,主要分为stub   hw_hal   a2dp   policy 等四大块功能.

兼容的方法为如下:

/*

兼容以前的设计,4.0实现一个中间层:hardware/libhardware_legacy/audio/audio_hw_hal.cpp

结构与其他的audio_hw.c大同小异,差别在于open方法:

 

[cpp] view plaincopystatic int legacy_adev_open(const hw_module_t* module, const char* name, 

                            hw_device_t** device) 

{ 

    ...... 

 

    ladev->hwif = createAudioHardware(); 

    if (!ladev->hwif) { 

        ret = -EIO; 

        goto err_create_audio_hw; 

    } 

      ...... 

} 

看到那个熟悉的createAudioHardware()没有?这是以前我提到的Vendor Specific Audio接口,

然后新的接口再调用ladev->hwif的函数就是了。

因此老一套的alsa-libalsa-utilsalsa_sound也可以照搬过来,这里的文件被编译成静态库的,

因此你需要修改alsa_sound里面的Android.mk文件,链接这个静态库。还有alsa_sound的命名空间原来是“android”,

现在需要改成“android_audio_legacy---注意!!!

*/

第四部分:ALSA管理.代码目录:

文件结构

承接frameworks层服务端的总体接口.

alsa_default.cpp 的内容,s_init s_open s_close等等, s_open里面调用了s_route,设置软硬件参数.

从内容上看这是amlogic 2.3之前的代码的现用程序.重点分析这一部分的数据流走向.

ALSA其实也就代表一种管理方法,简单地说就是这里的程序就是alsa , alsa就是这里.感觉和V4L2还是有一点小小的区别.

 

这部分代码 现在google tinyalsa来代替它,但是仍然保留了接口,可以供厂商不用tinyalsa(功能不够强大)时来代替它, 只需要改一下*.mk文件即可.其实看一下也就十个文件,也不是很复杂的.当然,上层flinger 下层 kernel 适配的地方同样要复杂一些.

 

ALSA要比v4l2复杂一些.它的内容主要是一些软件方法.v4l2的内容是一些标准的接口命令.

同样,由于现在版本是4.0,不用这部分代码了,而用了\external\tinyalsa\. tinyalsa则简单很多!

 

调用方法为: ./hardware/libhardware_legacy/audio/hardware/libhardware/modules/audio/device/samsung/tuna/audio/是同层的。

之一是legacy audio,用于兼容2.2时代的alsa_sound;之二是stub audio接口;之三是Samsung Tuna的音频抽象层实现。

调用层次:AudioFlinger -> audio_hw -> tinyalsa

 

 

以上除了ALSA部分以外,

全部是audio_hw 的处理内容,下面讲第四部分,具体的执行者,tinyslsa.

 

 

第五部分:具体执行者 TinyALSA的分析.

1: 下文件asoundlib.h. 功能如下

其中:  为主体实现文件, c语言编写的应用工具.

仅仅两个文件pcm.cmixer.c,看来TinyALSA 确实要简单很多!

而且两个文件都不大,加起来1200行代码. mixer.c主要是一些ioctl open memset 等函数.

:ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);

 

 Pcm.c 主要是对数据流的一些操作;

主要函数有: pcm_bytes_to_frames; pcm_frames_to_bytes;

pcm_hw_mmap_status(struct pcm *pcm) 中以下两个函数是关键函数.

pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,

                             MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);

 

munmap(pcm->mmap_status, page_size);

mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap执行相反的操作,删除特定地址区域的对象映射。

 

pcm_mmap_write à pcm_mmap_write_areasà pcm_areas_copyà memcpy.

Mmap的写过程就是以上.

 

pcm_mmap_commit à pcm_sync_ptr à ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr);

这就是几个关键的pcm 流操作函数, 包括memcpy 之内,最终由底层硬件实现具体功能.

而音频的响应速度,可以测一下 memcpy 这里的用法,看有没有启用cache .

 

当然最重要的函数为: pcm_read  pcm_write.

最终通过ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE);操作pcm->fd;

具体就到了/kernel/sound/core/pcm_native.c里面了.

如有疑问请参见以下网址: http://blog.csdn.net/myarrow/article/details/8228437

这个网址所说的alsa库调用方法,需要我们认真掌握.

附记一点,kernel 里面也是可以使用ioctlsnd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);

我这里在kernel中搜出来SNDRV_PCM_IOCTL_START的结果:

可见\kernel\include\sound\Asound.h 这个头文件基本上定义了,所有的ALSA接口命令.

 

 

到目前为止没有看到音频格式转化函数.

 

 

第六部分: aml Kernel 层代码分析

1:代码树

 

2:代码功能模块

3:硬件数据流及名词解析

4: ALSA设备文件结构

    我们从alsalinux中的设备文件结构开始我们的alsa之旅. 看看我的电脑中的alsa驱动的设备文件结构:

$ cd /dev/snd
$ ls -l

crw-rw----+ 1 root audio 116, 8 2011-02-23 21:38 controlC0
crw-rw----+ 1 root audio 116, 4 2011-02-23 21:38 midiC0D0
crw-rw----+ 1 root audio 116, 7 2011-02-23 21:39 pcmC0D0c
crw-rw----+ 1 root audio 116, 6 2011-02-23 21:56 pcmC0D0p
crw-rw----+ 1 root audio 116, 5 2011-02-23 21:38 pcmC0D1p
crw-rw----+ 1 root audio 116, 3 2011-02-23 21:38 seq
crw-rw----+ 1 root audio 116, 2 2011-02-23 21:38 timer

    我们可以看到以下设备文件:

  • controlC0 -->                 用于声卡的控制,例如通道选择,混音,麦克风的控制等
  • midiC0D0  -->                用于播放midi音频
  • pcmC0D0c --               用于录音的pcm设备
  • pcmC0D0p --               用于播放的pcm设备
  • seq  --                        音序器
  • timer --                       定时器

其中,C0D0代表的是声卡0中的设备0pcmC0D0c最后一个c代表capturepcmC0D0p最后一个p代表playback,这些都是alsa-driver中的命名规则。从上面的列表可以看出,我的声卡下挂了6个设备,根据声卡的实际能力,驱动实际上可以挂上更多种类的设备,在 include/sound/core.h中,定义了以下设备类型:

1.        #define SNDRV_DEV_TOPLEVEL  ((__force snd_device_type_t) 0)  

2.        #define SNDRV_DEV_CONTROL   ((__force snd_device_type_t) 1)  

3.        #define SNDRV_DEV_LOWLEVEL_PRE  ((__force snd_device_type_t) 2)  

4.        #define SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000)  

5.        #define SNDRV_DEV_PCM       ((__force snd_device_type_t) 0x1001)  

6.        #define SNDRV_DEV_RAWMIDI   ((__force snd_device_type_t) 0x1002)  

7.        #define SNDRV_DEV_TIMER     ((__force snd_device_type_t) 0x1003)  

8.        #define SNDRV_DEV_SEQUENCER ((__force snd_device_type_t) 0x1004)  

9.        #define SNDRV_DEV_HWDEP     ((__force snd_device_type_t) 0x1005)  

10.      #define SNDRV_DEV_INFO      ((__force snd_device_type_t) 0x1006)  

11.      #define SNDRV_DEV_BUS       ((__force snd_device_type_t) 0x1007)  

12.      #define SNDRV_DEV_CODEC     ((__force snd_device_type_t) 0x1008)  

13.      #define SNDRV_DEV_JACK          ((__force snd_device_type_t) 0x1009)  

14.      #define SNDRV_DEV_LOWLEVEL  ((__force snd_device_type_t) 0x2000)  

 通常,我们更关心的是pcmcontrol这两种设备。

 5:---驱动,后续再分析,主要查看内存,及格式操作即可.也可以用通用的程序测试.

 

 

 

 

 

第七部分: FrameWorks 层代码分析

1:文件架构

文件说明: 由下至上.

audioflinger 当然是核心处理文件.

         Mediaserver 多媒体交互文件

         Libstagefright 取代老的opencore 程序功能.

         Libeffects 音效处理类.

 

         Libmedia:重点文件夹(重点说明):

             BP(binder proxy)BN(binder native)是通过binder来通信的。Bp主要是用来处理java层传下来的服务请求。然后通过transact将处理请求传给bn(通过binder)。

         打开里面的这个文件夹,可以看到Bp*(承接java 本地端)  Bn*(c++ 远程服务端) 以及通用的流控制文件都在这里. 因些可以断定audio jni 下来之后到Hal frameworks 层的主要代码的主要代码都在这里.

           包含了native binder的两端 本地 ------- 服务 的代码. 测试录音时,可以在这里增加对帧速的测试点. 同时在Hal层也设立测试点, 这样基本上可以判断是哪一层的音频处理的问题了.

        

 右边这个是audio java库架层代码,jni 上面一层的代码. 给应用提供底层接口.

        

         再右边这个,不用解释 binder jni.

        

        

 

 

 

 

2:核心audioFlinger架构文件分析

默认的情况下,Android放音的采样率固定为44.1khz,录音的采样率固定为8khz,因此底层的音频设备驱动只需设置好这两个固定的采样率。如果上层传过来的采样率与其不符的话,则Android Framework层会对音频流做resample(重采样)处理。具体重采样的文章可以参阅以下文章: http://blog.csdn.net/zoe6553/article/details/7199543

排除掉重采样,可以发现AudioFlinger模块核心是四部分,buffer flinger mixer Policy;

具体分析flinger部分.

以上为audioflinger总的类图关系.

 

有以下类及主要功能:

.class AudioFlinger : public BinderService<AudioFlinger>, public BnAudioFlinger

   包括音频数据流和命令流的主要操作.

  以下都是他的类中类. 嵌套类中说明的成员不是外围类中对象的成员,反之亦然。嵌套类的成员函数对外围类的成员没有访问权,反之亦然。国此,在分析嵌套类和外围类的成员访问关系时,往往把嵌套类看作非嵌套类来处理。

 

.class Client : public RefBase

本地端通信功能.

 

.class NotificationClient : public IBinder::DeathRecipient

用于binder通信事件通知处理类.

 

.class ThreadBase : public Thread

他包含四个子类: class TrackBase : public AudioBufferProvider, public RefBase

           class ConfigEvent

           class PMDeathRecipient : public IBinder::DeathRecipient

class SuspendedSessionDesc : public RefBase

   音频frameworks层的总的线程管理程序.

 

.class PlaybackThread : public ThreadBase

他包含2个子类: class Track : public TrackBase

                           如图OutputTrack

                           

   播放线程.                       

 

.class MixerThread : public PlaybackThread

   播放中的mixer操作.

 

.class DirectOutputThread : public PlaybackThread

   先看看AudioFlinger::PlaybackThread::addEffectChain_l函数中的处理,
 
函数中根据是否为DIRECT output thread,有两种处理方式:
 
一种是处理direct output thread:
调用函数setMainBufferPlaybackThreadmMixBuffer告诉给Track,即告诉Track,在AudioMixer中往PlaybackThreadmMixBuffercopy数据。然后将effect  chaininput bufferoutput buffer都设置为PlaybackThreadmMixBuffer。(目的是让该effect chain不起作用?存在注释“Only one effect chain can be present in direct output thread and it usesthe mix buffer as input”
另一种是处理非direct output thread:
new
一段buffer出来。调用函数setMainBuffer将新buffer告诉给Track,即告诉Track,在AudioMixer中往新buffercopy数据。
调用函数setInBuffer来实装chaininput buffer。(发现函数AudioFlinger::PlaybackThread::createTrack_l中其实有将chaininput buffer赋值给Trackmain buffer)。然后将PlaybackThreadmMixBuffer赋值给chainoutput buffer
也就是说,Track将数据copy到自己的main buffer(effect chaininput buffer)effect chain对数据进行处理,然后将处理过的数据赋值给自己的output buffer(PlaybackThreadmMixBuffer)
mMixBuffer
是如何被使用的呢?

 

从上可以看出是数据的copy的一种方式.

 

.class DuplicatingThread : public MixerThread

  AudioFlinger中有一个特殊的线程类:DuplicatingThread,从图可以知道,它是MixerThread的子类。当系统中有两个设备要同时输出时,DuplicatingThread将被创建,通过IAudioFlingeropenDuplicateOutput方法创建DuplicatingThread

 

.class TrackHandle : public android::BnAudioTrack

 

  对音频播放的操作.

  问题:这里的start stop flinger 里面的start stop 有何不同.

  :不同的内容.flinger类并没有这两个方法,在其子类中有.

  从这里看出,flinger 并不只是对数据流进行操作,还提供了给上面应用的调用的接口.

 

.class RecordThread : public ThreadBase, public AudioBufferProvider

他包含子类: class RecordTrack : public TrackBase

录音的流程和放音差不多,只不过数据流动的方向相反,录音线程变成RecordThread,Track变成了

RecordTrack,openRecord返回RecordHandle.

 

.class RecordHandle : public android::BnAudioRecord

   TrackHandle相反的数据流操作.

 

.class EffectModule: public RefBase

.class EffectChain: public RefBase 及子类class SuspendedEffectDesc : public RefBase

对音效的修正.

 

.struct AudioStreamOut

.struct AudioStreamIn

.struct AudioSessionRef

 

/*  AudioTrack对象的创建过程时,了解到,创建一个AudioTrack对象,必须指定一个SessionId,并与其他使用该SessionIdAudioTrackMediaPlayer共享AudioEffect

如果不指定SessionId,将会自动生成一个SessionIdAudioEffect会将该SessionId与新创建的AudioTrack对象关联起来。 别人可以通过getAudioSessionId函数取得该SessionId

*/

 

还加入友元类

friend class RecordThread;

friend class PlaybackThread;