alsa sample rate跟踪 <3>
接着看。
还有一个疑问点,按照之前的分析,如果想要snd_pcm_hw_params被调用,需要调用snd_pcm_rate_open。
但是从上面列出来的函数调用关系没有调用snd_pcm_rate_open,那么这个东东是什么时候被调用的呢?
原来在函数snd_pcm_plug_hw_params中有个判断,如果client params中的参数与slave params中的不一致,将调用snd_pcm_plug_insert_plugins函数。
static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_plug_t *plug = pcm->private_data; snd_pcm_t *slave = plug->req_slave; snd_pcm_plug_params_t clt_params, slv_params; snd_pcm_hw_params_t sparams; int err; err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams); if (err < 0) return err; err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams); if (err < 0) return err; err = snd_pcm_hw_refine_soft(slave, &sparams); if (err < 0) return err; INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access); INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format); INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels); INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0); INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format); INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels); INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0); snd_pcm_plug_clear(pcm); if (!(clt_params.format == slv_params.format && clt_params.channels == slv_params.channels && clt_params.rate == slv_params.rate && !plug->ttable && snd_pcm_hw_params_test_access(slave, &sparams, clt_params.access) >= 0)) { INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access); err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params); if (err < 0) return err; } slave = plug->gen.slave; err = _snd_pcm_hw_params(slave, params); if (err < 0) { snd_pcm_plug_clear(pcm); return err; } snd_pcm_unlink_hw_ptr(pcm, plug->req_slave); snd_pcm_unlink_appl_ptr(pcm, plug->req_slave); snd_pcm_link_hw_ptr(pcm, slave); snd_pcm_link_appl_ptr(pcm, slave); return 0; }
函数snd_pcm_plug_insert_plugins中会通过一个while循环,依次调用plugin进行处理,知道client params和slave params完全一致。
static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, snd_pcm_plug_params_t *client, snd_pcm_plug_params_t *slave) { snd_pcm_plug_t *plug = pcm->private_data; static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = { #ifdef BUILD_PCM_PLUGIN_MMAP_EMUL snd_pcm_plug_change_mmap, #endif snd_pcm_plug_change_format, #ifdef BUILD_PCM_PLUGIN_ROUTE snd_pcm_plug_change_channels, #endif #ifdef BUILD_PCM_PLUGIN_RATE snd_pcm_plug_change_rate, #endif #ifdef BUILD_PCM_PLUGIN_ROUTE snd_pcm_plug_change_channels, #endif snd_pcm_plug_change_format, snd_pcm_plug_change_access }; snd_pcm_plug_params_t p = *slave; unsigned int k = 0; plug->ttable_ok = plug->ttable_last = 0; while (client->format != p.format || client->channels != p.channels || client->rate != p.rate || client->access != p.access) { snd_pcm_t *new; int err; if (k >= sizeof(funcs)/sizeof(*funcs)) return -EINVAL; err = funcs[k](pcm, &new, client, &p); if (err < 0) { snd_pcm_plug_clear(pcm); return err; } if (err) { plug->gen.slave = new; pcm->fast_ops = new->fast_ops; pcm->fast_op_arg = new->fast_op_arg; } k++; } ... }
其中的snd_pcm_plug_change_rate函数调用了snd_pcm_rate_open函数。
函数snd_pcm_plug_insert_plugins中定义了一个snd_pcm_t指针:snd_pcm_t *new,并将其传给了snd_pcm_plug_change_rate。
snd_pcm_plug_change_rate又将new传给了snd_pcm_rate_open函数。
snd_pcm_rate_open函数中将snd_pcm_rate_ops赋值给了new的ops成员:
pcm->ops = &snd_pcm_rate_ops;
snd_pcm_plug_insert_plugins中判断如果处理都成功,将new赋值给plug->gen.slave:
snd_pcm_plug_t *plug = pcm->private_data; plug->gen.slave = new;
再回到函数snd_pcm_plug_hw_params,其中在调用过snd_pcm_plug_insert_plugins之后,将plug->gen.slave赋值给了slave。
然后以slave为参数调用了函数_snd_pcm_hw_params:
snd_pcm_plug_t *plug = pcm->private_data; slave = plug->gen.slave; err = _snd_pcm_hw_params(slave, params);
函数_snd_pcm_hw_params中会调用slave中ops的hw_params函数:
err = pcm->ops->hw_params(pcm->op_arg, params);
看到这儿,发现alsa lib中通常是以pcm为媒介,在open的时候将根据需要将函数指针挂到pcm上。
后面要用的时候再调用pcm上的函数指针。
既然这样,我们就看看在前面的open流程中都往pcm上挂了哪些东东。