音频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-lib、alsa-utils和alsa_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.c和mixer.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 里面也是可以使用ioctl的snd_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设备文件结构
我们从alsa在linux中的设备文件结构开始我们的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中的设备0,pcmC0D0c最后一个c代表capture,pcmC0D0p最后一个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)
通常,我们更关心的是pcm和control这两种设备。
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:
调用函数setMainBuffer将PlaybackThread的mMixBuffer告诉给Track,即告诉Track,在AudioMixer中往PlaybackThread的mMixBuffer中copy数据。然后将effect chain的input buffer和output buffer都设置为PlaybackThread的mMixBuffer。(目的是让该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中往新buffer中copy数据。
调用函数setInBuffer来实装chain的input buffer。(发现函数AudioFlinger::PlaybackThread::createTrack_l中其实有将chain的input buffer赋值给Track的main buffer)。然后将PlaybackThread的mMixBuffer赋值给chain的output buffer。
也就是说,Track将数据copy到自己的main buffer(即effect chain的input buffer),effect chain对数据进行处理,然后将处理过的数据赋值给自己的output buffer(即PlaybackThread的mMixBuffer)
mMixBuffer是如何被使用的呢?
从上可以看出是数据的copy的一种方式.
⑧.class DuplicatingThread : public MixerThread
AudioFlinger中有一个特殊的线程类:DuplicatingThread,从图可以知道,它是MixerThread的子类。当系统中有两个设备要同时输出时,DuplicatingThread将被创建,通过IAudioFlinger的openDuplicateOutput方法创建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,并与其他使用该SessionId的AudioTrack和MediaPlayer共享AudioEffect。
如果不指定SessionId,将会自动生成一个SessionId,AudioEffect会将该SessionId与新创建的AudioTrack对象关联起来。 别人可以通过getAudioSessionId函数取得该SessionId。
*/
还加入友元类
friend class RecordThread;
friend class PlaybackThread;