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上挂了哪些东东。