赞
踩
无论是在录音还是在放音,都要打开一个PCM流,具体对应的函数原型为:
- int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode);
本文以录音为例介绍一下它的流程。录音时传入name==AndroidCapture
先解一下各个参数的函义:
首先会根据传入的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到配置树查找到的结点。
- 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;
- }
分析传递过来的配置树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是否包含在:
- 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中定义:
- 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的配置项如下:
- pcm.AndroidPlayback_Speaker_normal {
- type plug
- slave.pcm {
- type hw
- card 0
- device 0
- }
- }
对于此节点,使用show_snd_config结果如下:
- 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);生成。
- /**
- * \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.
_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的初始化代码如下:
- //创建类型为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;
-
配置树如下图所示:
转自:http://blog.chinaunix.net/uid-27134408-id-3272774.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。