您的位置:首页 > 其它

alsa-lib如何解析asound.conf

2014-04-21 15:19 176 查看
无论是在录音还是在放音,都要打开一个PCM流,具体对应的函数原型为:

[cpp] view
plaincopy

int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode);

本文以录音为例介绍一下它的流程。录音时传入name==AndroidCapture

先解一下各个参数的函义:

pcmp: 是带回的pcm流
name是要找开的流即放音还是录音,放音对应AndroidPlayback_Speaker_normal,录音对应AndroidCapture。放音的字符串会因为当前使用设备和设备状况不一样而有所不同,不同之处在下划线后面那部分。录音则因为大多数板子成型后,都只有一个录音设备或者接口,因此录音串对应的就是AndroidCapture
stream对应流的类型,是放音(0)还是录音(1).
mode是指设备打开模式,阻塞或非阻塞。


2. 打开流程(snd_pcm_open_conf)

首先会根据传入的name,到配置树中查找对应的结点。 snd_config_search_definition(root, "pcm", name, &pcm_conf); 此函数将在配置树中查找对应的结点,然后将查找到的结点复制一份到

pcm_conf中。 之后,系统会调用snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);进一步分析,pcm_conf即为刚才根据传入name到配置树查找到的结点。

[cpp] view
plaincopy

static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,

snd_config_t *pcm_root, snd_config_t *pcm_conf,

snd_pcm_stream_t stream, int mode)

{

const char *str;

char *buf = NULL, *buf1 = NULL;

int err;

snd_config_t *conf, *type_conf = NULL;

snd_config_iterator_t i, next;

const char *id;

const char *lib = NULL, *open_name = NULL;

int (*open_func)(snd_pcm_t **, const char *,

snd_config_t *, snd_config_t *,

snd_pcm_stream_t, int) = NULL;

#ifndef PIC

extern void *snd_pcm_open_symbols(void);

#endif

void *h = NULL;

if (snd_config_get_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) {

char *val;

id = NULL;

snd_config_get_id(pcm_conf, &id);

val = NULL;

snd_config_get_ascii(pcm_conf, &val);

SNDERR("Invalid type for PCM %s%sdefinition (id: %s, value: %s)", name ? name : "", name ? " " : "", id, val);

free(val);

return -EINVAL;

}

err = snd_config_search(pcm_conf, "type", &conf);

if (err < 0) {

SNDERR("type is not defined");

return err;

}

err = snd_config_get_id(conf, &id);

if (err < 0) {

SNDERR("unable to get id");

return err;

}

err = snd_config_get_string(conf, &str);

if (err < 0) {

SNDERR("Invalid type for %s", id);

return err;

}

err = snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf);

if (err >= 0) {

if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {

SNDERR("Invalid type for PCM type %s definition", str);

goto _err;

}

snd_config_for_each(i, next, type_conf) {

snd_config_t *n = snd_config_iterator_entry(i);

const char *id;

if (snd_config_get_id(n, &id) < 0)

continue;

if (strcmp(id, "comment") == 0)

continue;

if (strcmp(id, "lib") == 0) {

err = snd_config_get_string(n, &lib);

if (err < 0) {

SNDERR("Invalid type for %s", id);

goto _err;

}

continue;

}

if (strcmp(id, "open") == 0) {

err = snd_config_get_string(n, &open_name);

if (err < 0) {

SNDERR("Invalid type for %s", id);

goto _err;

}

continue;

}

SNDERR("Unknown field %s", id);

err = -EINVAL;

goto _err;

}

}

if (!open_name) {

buf = malloc(strlen(str) + 32);

if (buf == NULL) {

err = -ENOMEM;

goto _err;

}

open_name = buf;

sprintf(buf, "_snd_pcm_%s_open", str); //生成真正的open函数

}

if (!lib) {

const char *const *build_in = build_in_pcms;

while (*build_in) {

if (!strcmp(*build_in, str))

break;

build_in++;

}

if (*build_in == NULL) {

buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);

if (buf1 == NULL) {

err = -ENOMEM;

goto _err;

}

lib = buf1;

sprintf(buf1, "%s/libasound_module_pcm_%s.so", ALSA_PLUGIN_DIR, str);

}

}

#ifndef PIC

snd_pcm_open_symbols(); /* this call is for static linking only */

#endif

open_func = snd_dlobj_cache_lookup(open_name);

if (open_func) {

err = 0;

goto _err;

}

h = snd_dlopen(lib, RTLD_NOW);

if (h)

open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION));

err = 0;

if (!h) {

SNDERR("Cannot open shared library %s",

lib ? lib : "[builtin]");

err = -ENOENT;

} else if (!open_func) {

SNDERR("symbol %s is not defined inside %s", open_name,

lib ? lib : "[builtin]");

snd_dlclose(h);

err = -ENXIO;

}

_err:

if (err >= 0) {

err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);

if (err >= 0) {

if (h /*&& (mode & SND_PCM_KEEP_ALIVE)*/) {

snd_dlobj_cache_add(open_name, h, open_func);

h = NULL;

}

(*pcmp)->dl_handle = h;

err = 0;

} else {

if (h)

snd_dlclose(h);

}

}

if (type_conf)

snd_config_delete(type_conf);

free(buf);

free(buf1);

return err;

}

snd_pcm_open_conf函数的主要功能:

分析传递过来的配置树pcm_conf的type子结点对应的字符串:hooks,然后调用由type值(hooks)构成的字符串函数(_snd_pcm_hooks_open)的过程。在我们系统中后面还要调用此函数,而且在查找lib名时,都能在build_in_pcms中找到,所以对我们来说这个函数的主要功能就是查找pcm_conf子结点type对应的字符串<如plug>构成的函数。

snd_pcm_open_conf会根据配置树的情况,获取子节点中名为type的结点(它是一个叶子结点),并获取它的值hooks,使用str变量存储。然后会在root对应配置树中查找名为pcm_type hooks的结点。查找成功后,会在查找到的结点中搜索所索要的lib,open_name字符串。(后面要打开)。 在本系统中没有找到pcm_type hooks的结点。那么lib和open_name均为空,下面就要对lib/open_name初始化,(如果找到pcm_type
empyt结点则会用该结点的孩子结点初始化)。首先对lib初始化,首先查找str是否包含在:

[cpp] view
plaincopy

static const char *const build_in_pcms[] = {

"adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat",

"linear", "meter", "mulaw", "multi", "null", "empty", "plug", "rate", "route", "share",

"shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul",

NULL

};

这个数组中,如果在,就将lib设为null.如果不在,设置lib为默认库

sprintf(lib, "%s/libasound_module_pcm_%s.so", ALSA_PLUGIN_DIR, str); 即/usr/lib/alsa-lib/libasound_module_pcm_hooks.so;如果查找发现hooks在数组中,所以lib为null。下面对open_name初始化sprintf(open_name, "_snd_pcm_%s_open", open_name);即_snd_pcm_hooks_open函数。

为了搞清楚配置选项在代码中的表现形式,可用如下码查看,此函数在conf.c中定义:

[cpp] view
plaincopy

int show_snd_config(snd_config_t *config)

{

assert(config);

switch (config->type) {

case SND_CONFIG_TYPE_COMPOUND:

{

int err;

struct list_head *i;

i = config->u.compound.fields.next;

SNDERR("Start Compound---> node id =%s\n",config->id?config->id:"null");

while (i != &config->u.compound.fields) {

struct list_head *nexti = i->next;

snd_config_t *leaf = snd_config_iterator_entry(i);

err = show_snd_config(leaf);

if (err < 0)

return err;

i = nexti;

}

SNDERR("End Compound---> ---> node id =%s\n",config->id?config->id:"null");

break;

}

case SND_CONFIG_TYPE_STRING:

SNDERR("SND_CONFIG_TYPE_STRING---> leaf id=%s,str=%s\n",config->id?config->id:"null",config->u.string?config->u.string:"null");

break;

case SND_CONFIG_TYPE_INTEGER:

SNDERR("SND_CONFIG_TYPE_ INTGER --->leaf id=%s,num=%ld\n",config->id?config->id:"null",config->u.integer);

break;

case SND_CONFIG_TYPE_REAL:

SNDERR("SND_CONFIG_TYPE_ REAL--->leaf id=%s,num=%f\n",config->id?config->id:"null",config->u.real);

break;

case SND_CONFIG_TYPE_INTEGER64:

SNDERR("SND_CONFIG_TYPE_INTEGER64--->leaf id=%s,num=%ld\n",config->id?config->id:"null",config->u.integer64);

break;

case SND_CONFIG_TYPE_POINTER:

SNDERR("SND_CONFIG_TYPE_POINTER--->leaf id=%s,num=%p\n",config->id?config->id:"null",config->u.ptr);

break;

default:

break;

}

return 0;

}

对于音频播放,其在asound.conf的配置项如下:

[cpp] view
plaincopy

pcm.AndroidPlayback_Speaker_normal {

type plug

slave.pcm {

type hw

card 0

device 0

}

}

对于此节点,使用show_snd_config结果如下:

[cpp] view
plaincopy

pcm/pcm.c:2280:(snd_pcm_open) ***2280:start:name:AndroidPlayback_Speaker_normal

pcm/pcm.c:2235:(snd_pcm_open_noupdate) ***Begin:2235:name=AndroidPlayback_Speaker_normal, stream type:0

pcm/pcm.c:2249:(snd_pcm_open_noupdate) 2222222 pcm_conf

conf.c:2504:(show_snd_config) Start Compound---> node id =AndroidPlayback_Speaker_normal

conf.c:2520:(show_snd_config) SND_CONFIG_TYPE_STRING---> leaf id=type,str=plug

conf.c:2504:(show_snd_config) Start Compound---> node id =slave

conf.c:2504:(show_snd_config) Start Compound---> node id =pcm

conf.c:2520:(show_snd_config) SND_CONFIG_TYPE_STRING---> leaf id=type,str=hw

conf.c:2524:(show_snd_config) SND_CONFIG_TYPE_ INTGER --->leaf id=card,num=0

conf.c:2524:(show_snd_config) SND_CONFIG_TYPE_ INTGER --->leaf id=device,num=0

conf.c:2516:(show_snd_config) End Compound---> ---> node id =pcm

conf.c:2516:(show_snd_config) End Compound---> ---> node id =slave

conf.c:2516:(show_snd_config) End Compound---> ---> node id =AndroidPlayback_Speaker_normal

pcm/pcm.c:2260:(snd_pcm_open_noupdate) ***2260:name=AndroidPlayback_Speaker_normal,hop=0

pcm/pcm.c:2079:(snd_pcm_open_conf) ***Begin: 2079

pcm/pcm.c:2100:(snd_pcm_open_conf) ***2100:id=type

pcm/pcm.c:2107:(snd_pcm_open_conf) ***2107:str=plug

pcm/pcm.c:2152:(snd_pcm_open_conf) ***2152:buf=_snd_pcm_plug_open

pcm/pcm.c:2156:(snd_pcm_open_conf) ***2156:str=plug

pcm/pcm.c:2177:(snd_pcm_open_conf) ***2177:open_name=_snd_pcm_plug_open

pcm/pcm.c:2199:(snd_pcm_open_conf) ***2199:cal open_func<_snd_pcm_plug_open>

pcm/pcm_plug.c:1227:(_snd_pcm_plug_open) ****Begin:1227

pcm/pcm.c:2079:(snd_pcm_open_conf) ***Begin: 2079

pcm/pcm.c:2100:(snd_pcm_open_conf) ***2100:id=type

pcm/pcm.c:2107:(snd_pcm_open_conf) ***2107:str=hw

pcm/pcm.c:2152:(snd_pcm_open_conf) ***2152:buf=_snd_pcm_hw_open

pcm/pcm.c:2156:(snd_pcm_open_conf) ***2156:str=hw

pcm/pcm.c:2177:(snd_pcm_open_conf) ***2177:open_name=_snd_pcm_hw_open

pcm/pcm.c:2199:(snd_pcm_open_conf) ***2199:call open_func<_snd_pcm_hw_open>

pcm/pcm_hw.c:1397:(_snd_pcm_hw_open) ***Begin:1397

pcm/pcm_hw.c:1272:(snd_pcm_hw_open) ***Begin:1272:err=0

pcm/pcm_hw.c:1273:(snd_pcm_hw_open) ***1273:name=(null),card=0,device=0,subdevice=-1

control/control_hw.c:375:(snd_ctl_hw_open) ***375:card:0

pcm/pcm_hw.c:1129:(snd1_pcm_hw_open_fd) ***Begin:1129:ret=0

pcm/pcm_hw.c:1237:(snd1_pcm_hw_open_fd) ***End:1237:ret=0

pcm/pcm_hw.c:1327:(snd_pcm_hw_open) ***End:1327:ret=0

pcm/pcm_hw.c:1519:(_snd_pcm_hw_open) ***Begin:1519:err=0

pcm/pcm.c:2218:(snd_pcm_open_conf) ***END: 2218:err=0

pcm/pcm_plug.c:1328:(_snd_pcm_plug_open) ****End: 1328: err=0

pcm/pcm.c:2218:(snd_pcm_open_conf) ***END: 2218:err=0

pcm/pcm.c:2264:(snd_pcm_open_noupdate) ***End:2264:err=0

所以与此节点对应的open函数为_snd_pcm_plug_open,通过函数sprintf(buf, "_snd_pcm_%s_open", str);生成。


3. _snd_pcm_hooks_open <如_snd_pcm_plug_open>

[cpp] view
plaincopy

/**

* \brief Creates a new Plug PCM

* \param pcmp Returns created PCM handle

* \param name Name of PCM

* \param root Root configuration node

* \param conf Configuration node with Plug PCM description

* \param stream Stream type

* \param mode Stream mode

* \retval zero on success otherwise a negative error code

* \warning Using of this function might be dangerous in the sense

* of compatibility reasons. The prototype might be freely

* changed in future.

*/

int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,

snd_config_t *root, snd_config_t *conf,

snd_pcm_stream_t stream, int mode)

{

snd_config_iterator_t i, next;

int err;

snd_pcm_t *spcm;

snd_config_t *slave = NULL, *sconf;

snd_config_t *tt = NULL;

enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;

snd_pcm_route_ttable_entry_t *ttable = NULL;

unsigned int csize, ssize;

unsigned int cused, sused;

snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;

int schannels = -1, srate = -1;

const snd_config_t *rate_converter = NULL;

SNDERR("****Begin:%d\n",__LINE__);

snd_config_for_each(i, next, conf) {

snd_config_t *n = snd_config_iterator_entry(i);

const char *id;

if (snd_config_get_id(n, &id) < 0)

continue;

if (snd_pcm_conf_generic_id(id))

continue;

if (strcmp(id, "slave") == 0) {

slave = n;

continue;

}

#ifdef BUILD_PCM_PLUGIN_ROUTE

if (strcmp(id, "ttable") == 0) {

route_policy = PLUG_ROUTE_POLICY_NONE;

if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {

SNDERR("Invalid type for %s", id);

return -EINVAL;

}

tt = n;

continue;

}

if (strcmp(id, "route_policy") == 0) {

const char *str;

if ((err = snd_config_get_string(n, &str)) < 0) {

SNDERR("Invalid type for %s", id);

return -EINVAL;

}

if (tt != NULL)

SNDERR("Table is defined, route policy is ignored");

if (!strcmp(str, "default"))

route_policy = PLUG_ROUTE_POLICY_DEFAULT;

else if (!strcmp(str, "average"))

route_policy = PLUG_ROUTE_POLICY_AVERAGE;

else if (!strcmp(str, "copy"))

route_policy = PLUG_ROUTE_POLICY_COPY;

else if (!strcmp(str, "duplicate"))

route_policy = PLUG_ROUTE_POLICY_DUP;

continue;

}

#endif

#ifdef BUILD_PCM_PLUGIN_RATE

if (strcmp(id, "rate_converter") == 0) {

rate_converter = n;

continue;

}

#endif

SNDERR("Unknown field %s", id);

return -EINVAL;

}

if (!slave) {

SNDERR("slave is not defined");

return -EINVAL;

}

err = snd_pcm_slave_conf(root, slave, &sconf, 3,

SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,

SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,

SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);

if (err < 0){

SNDERR("****End: %d: err=%d\n",__LINE__,err);

return err;

}

#ifdef BUILD_PCM_PLUGIN_ROUTE

if (tt) {

err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);

if (err < 0) {

snd_config_delete(sconf);

SNDERR("****End: %d: err=%d\n",__LINE__,err);

return err;

}

ttable = malloc(csize * ssize * sizeof(*ttable));

if (ttable == NULL) {

snd_config_delete(sconf);

SNDERR("****End: %d: err=%d\n",__LINE__,err);

return err;

}

err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);

if (err < 0) {

snd_config_delete(sconf);

SNDERR("****End: %d: err=%d\n",__LINE__,err);

return err;

}

}

#endif

#ifdef BUILD_PCM_PLUGIN_RATE

if (! rate_converter)

rate_converter = snd_pcm_rate_get_default_converter(root);

#endif

err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);

snd_config_delete(sconf);

if (err < 0){

SNDERR("****End: %d: err=%d\n",__LINE__,err);

return err;

}

err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,

route_policy, ttable, ssize, cused, sused, spcm, 1);

if (err < 0)

snd_pcm_close(spcm);

SNDERR("****End: %d: err=%d\n",__LINE__,err);

return err;

}

这个函数主要完成三件事:

1)在传入的配置树中查找子孩子ID为slave的结点

2)然后调用snd_pcm_open_slave和snd_pcm_hooks_open

3)最后再分析配置树是否有hooks结点,有的话调用snd_pcm_hook_add_conf。

为了方便大家理解,还是先看一下传入_snd_pcm_hooks_open配置树的情况, 先来看一下snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);函数,在这里sconf即为slave结点中ID为pcm对应的配置树。root对应的是总的配置树(系统启动时会根据配置文件asound.conf/alsa-lib.conf加载构建)。

snd_pcm_open_slave-->

snd_pcm_open_named_slave-->

snd_pcm_open_noupdate

由此可见snd_pcm_open_slave仍然会走到snd_pcm_open_noupdate这个函数处。然后会根据传入的参数name=default,在总的配置树中查找pcm default结点。snd_config_search_definition最终根据pcm default串查找对应的树。之后会调用 snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode)前面已经详细介过本函数了,本次调用只是传递过来的配置树不一样。

snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode)根据当前传入树pcm_conf的情况会调用如下函数_snd_pcm_empty_open。这个函数会从当前传入树取下slave孩子结点的pcm结点对应的配置树做为根结点,并调用 snd_pcm_open_named_slave.

snd_pcm_open_named_slave-->

snd_pcm_open_conf

可以看出又回到了snd_pcm_open_conf函数,只是传入的树不一样。snd_pcm_open_conf会调用_snd_pcm_plug_open.

函数_snd_pcm_plug_open会调 用snd_pcm_open_slave及snd_pcm_plug_open。

在传入树中有这样的子树,调用

snd_pcm_open_slave-->

nd_pcm_open_named_slave-->

snd_pcm_open_conf-->

_snd_pcm_hw_open.


4. _snd_pcm_hw_open

_snd_pcm_hw_open-->snd_pcm_hw_open,此函数中会根据树中的card号码,调用snd_ctl_hw_open打开/dev/snd/ControlC0,然后再打开/dev/snd/pcmC0D0c,完成后调用snd_ctl_close关闭/dev/snd/ControlC0.并调用snd_pcm_hw_open_fd,传入fd为打开的/dev/snd/pcmC0D0c.该函数完成PCM流的创建,首先创建pcm的私有数据hw
(snd_pcm_hw_t结构),然后用参数(参数获取是通过ioctl命令发送到fd,然后内核将请求信息带回最后赋值给hw的成员中)初始化它,接着创建pcm(_snd_pcm结构),并将hw赋值给pcm->private_data成员。最后再调用snd_pcm_hw_mmap_status/snd_pcm_hw_mmap_control两个函数完成映射。需要注意在这里pcm有一个类型,本处创建的pcm类型为SND_PCM_TYPE_HW.

对pcm的初始化代码如下:

[cpp] view
plaincopy

//创建类型为SND_PCM_TYPE_HW的pcm流,并完成一些基本初始化。

ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode);

if (ret < 0) {

free(hw);

close(fd);

return ret;

}

//初始化ops函数集

pcm->ops = &snd_pcm_hw_ops;

//初始化fast_ops函数集

pcm->fast_ops = &snd_pcm_hw_fast_ops;

pcm->private_data = hw;//如上所述私有数据赋值

pcm->poll_fd = fd;/dev/snd/pcmC0D0c

pcm->poll_events = info.stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;

pcm->monotonic = monotonic;



5. 配置树

配置树如下图所示:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: