alsa sample rate跟踪 <4>

来源:互联网 发布:龙少泛站群软件 编辑:程序博客网 时间:2024/05/16 01:26

alsa sample rate跟踪 <4>

接下来,要看看open流程中都往pcm上挂了哪些东东。
aplay的main函数中调用snd_pcm_open函数,并传入了一个snd_pcm_t指针handle的地址:

static snd_pcm_t *handle; err = snd_pcm_open(&handle, pcm_name, stream, open_mode);



这个时候handle还是干净的。

snd_pcm_open中将snd_pcm_t指针的地址直接传给了函数snd_pcm_open_noupdate:

 return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);



 
snd_pcm_open_noupdate中将snd_pcm_t指针的地址直接传给了函数snd_pcm_open_conf:

  err = snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);



  
snd_pcm_open_conf中将snd_pcm_t指针的地址直接传给了函数_snd_pcm_plug_open:

  // 此处 open_func 为_snd_pcm_plug_open函数  err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);



  
_snd_pcm_plug_open中定义了一个snd_pcm_t指针,并将其地址传给了函数snd_pcm_open_slave:

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



 
snd_pcm_open_slave中将snd_pcm_t指针的地址直接传给了函数snd_pcm_open_named_slave:

 return snd_pcm_open_named_slave(pcmp, NULL, root, conf, stream,     mode, parent_conf);



     
snd_pcm_open_named_slave中将snd_pcm_t指针的地址直接传给了函数snd_pcm_open_noupdate:

  return snd_pcm_open_noupdate(pcmp, root, str, stream, mode,          hop + 1);



      
snd_pcm_open_noupdate中将snd_pcm_t指针的地址直接传给了函数snd_pcm_open_conf:

  err = snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);



  
snd_pcm_open_conf中将snd_pcm_t指针的地址直接传给了函数_snd_pcm_dmix_open:

  // 此处 open_func 为_snd_pcm_dmix_open函数  err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);



 
_snd_pcm_dmix_open中处理:
定义slave_params结构体:

 struct slave_params params;



初始化slave_params结构体:

 /* the default settings, it might be invalid for some hardware */ params.format = SND_PCM_FORMAT_S16; params.rate = 48000;  // 原来这儿有个48000,怪不得asound.conf是否指定rate 48000都会调用rate plug。 params.channels = 2; params.period_time = -1; params.buffer_time = -1; bsize = psize = -1; params.periods = 3;



将snd_pcm_t指针的地址和params传给了函数snd_pcm_dmix_open:

 err = snd_pcm_dmix_open(pcmp, name, &dopen, ¶ms,    root, sconf, stream, mode);



    
snd_pcm_dmix_open函数中相关处理:
首先定义:

 snd_pcm_t *pcm = NULL, *spcm = NULL; snd_pcm_direct_t *dmix = NULL;



为dmix分配空间:

 dmix = calloc(1, sizeof(snd_pcm_direct_t));



对dmix成员进行赋值:

 dmix->ipc_key = opts->ipc_key; dmix->ipc_perm = opts->ipc_perm; dmix->ipc_gid = opts->ipc_gid; dmix->semid = -1; dmix->shmid = -1;



给pcm分配空间并初始化:

 ret = snd_pcm_new(&pcm, dmix->type = SND_PCM_TYPE_DMIX, name, stream, mode);



将snd_pcm_dmix_ops赋值给pcm的ops,并将dmix赋值给pcm的private_data:

 pcm->ops = &snd_pcm_dmix_ops; pcm->fast_ops = &snd_pcm_dmix_fast_ops; pcm->private_data = dmix;



初始化dmix另外一些成员:

 dmix->state = SND_PCM_STATE_OPEN; dmix->slowptr = opts->slowptr; dmix->max_periods = opts->max_periods; dmix->sync_ptr = snd_pcm_dmix_sync_ptr;



以spcm为参数调用snd_pcm_open_slave:

  ret = snd_pcm_open_slave(&spcm, root, sconf, stream,      mode | SND_PCM_NONBLOCK, NULL);



     
snd_pcm_open_slave的调用过程见上面_snd_pcm_plug_open的分析。
最终调用到了下面这一步:
snd_pcm_open_conf中将snd_pcm_t指针的地址直接传给了函数_snd_pcm_hw_open:

  // 此处 open_func 为_snd_pcm_hw_open函数  err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);



  
_snd_pcm_hw_open中将snd_pcm_t指针的地址直接传给了函数snd_pcm_hw_open:

 err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream,         mode | (nonblock ? SND_PCM_NONBLOCK : 0),         0, sync_ptr_ioctl);  



snd_pcm_hw_open中的处理:
调用snd_open_device打开设备:(snd_open_device中调用open实现设备打开)

 fd = snd_open_device(filename, fmode);  // 这儿的filename是dev目录下的设备文件,如:/dev/snd/pcmC1D1p



将snd_pcm_t指针的地址直接传给了函数snd_pcm_hw_open_fd:

 return snd_pcm_hw_open_fd(pcmp, name, fd, 0, sync_ptr_ioctl);



 
snd_pcm_hw_open_fd中的处理:
定义:

 snd_pcm_t *pcm = NULL; snd_pcm_hw_t *hw = NULL;



通过ioctl对fd进行一些操作。
为hw分配空间:

 hw = calloc(1, sizeof(snd_pcm_hw_t));



初始化hw成员:

 hw->version = ver; hw->card = info.card; hw->device = info.device; hw->subdevice = info.subdevice; hw->fd = fd; hw->sync_ptr_ioctl = sync_ptr_ioctl; /* no restriction */ hw->format = SND_PCM_FORMAT_UNKNOWN; hw->rate = 0; hw->channels = 0;



给pcm分配空间并初始化:

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



将snd_pcm_hw_ops赋值给pcm的ops,将hw赋值给pcm的private_data:

 pcm->ops = &snd_pcm_hw_ops; pcm->fast_ops = &snd_pcm_hw_fast_ops; pcm->private_data = hw; pcm->poll_fd = fd; pcm->poll_events = info.stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; pcm->monotonic = monotonic;



将pcm赋值给传入的snd_pcm_t指针的地址:

 *pcmp = pcm;



 
从snd_pcm_open_slave出来。
回到snd_pcm_dmix_open:

调用snd_pcm_direct_initialize_slave函数进行初始化:

  // params是_snd_pcm_dmix_open中定义并初始化的结构体  ret = snd_pcm_direct_initialize_slave(dmix, spcm, params);



函数snd_pcm_direct_initialize_slave的注释:

/* * this function initializes hardware and starts playback operation with * no stop threshold (it operates all time without xrun checking) * also, the driver silences the unused ring buffer areas for us */



snd_pcm_direct_initialize_slave中设置了hw_params的各种参数,然后调用了snd_pcm_hw_params函数:

 ret = snd_pcm_hw_params(spcm, hw_params);



将hw_params的参数保存到snd_pcm_dmix_open的dmix中:

 /* store some hw_params values to shared info */ dmix->shmptr->hw.format = snd_mask_value(hw_param_mask(hw_params, SND_PCM_HW_PARAM_FORMAT)); dmix->shmptr->hw.rate = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_RATE); dmix->shmptr->hw.buffer_size = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_BUFFER_SIZE); dmix->shmptr->hw.buffer_time = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_BUFFER_TIME); dmix->shmptr->hw.period_size = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_PERIOD_SIZE); dmix->shmptr->hw.period_time = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_PERIOD_TIME); dmix->shmptr->hw.periods = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_PERIODS);



然后调用了snd_pcm_sw_params函数:

 ret = snd_pcm_sw_params(spcm, sw_params);



接下来调用了snd_pcm_start:

 ret = snd_pcm_start(spcm);



 
从snd_pcm_direct_initialize_slave出来。
回到snd_pcm_dmix_open:

将spcm赋值给dmix:

  dmix->spcm = spcm;



给pcm的部分成员赋值:

 pcm->poll_fd = dmix->poll_fd; pcm->poll_events = POLLIN; /* it's different than other plugins */ pcm->mmap_rw = 1;



将pcm赋值给传入的snd_pcm_t指针的地址:

 *pcmp = pcm;


 

从snd_pcm_open_slave出来。 
回到_snd_pcm_plug_open:

_snd_pcm_plug_open中将snd_pcm_t指针的地址和spcm传给了函数snd_pcm_plug_open:

 err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,    route_policy, ttable, ssize, cused, sused, spcm, 1);



    
snd_pcm_plug_open函数中定义了一个snd_pcm_plug_t指针,并分配了snd_pcm_plug_t对象。
然后初始化snd_pcm_plug_t对象,并将该对象赋值给snd_pcm_t指针的private_data。
并将snd_pcm_plug_ops赋值给snd_pcm_t指针的ops成员。
代码列出来,可能更明了:

/** * \brief Creates a new Plug PCM * \param pcmp Returns created PCM handle * \param name Name of PCM * \param sformat Slave (destination) format * \param slave Slave PCM handle * \param close_slave When set, the slave PCM handle is closed with copy PCM * \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_pcm_format_t sformat, int schannels, int srate,        const snd_config_t *rate_converter,        enum snd_pcm_plug_route_policy route_policy,        snd_pcm_route_ttable_entry_t *ttable,        unsigned int tt_ssize,        unsigned int tt_cused, unsigned int tt_sused,        snd_pcm_t *slave, int close_slave){ snd_pcm_t *pcm; // 定义指针 snd_pcm_plug_t *plug; int err; assert(pcmp && slave); // 分配空间 plug = calloc(1, sizeof(snd_pcm_plug_t)); if (!plug)  return -ENOMEM; plug->sformat = sformat; plug->schannels = schannels; plug->srate = srate; plug->rate_converter = rate_converter; // 将slave pcm赋值过来 plug->gen.slave = plug->req_slave = slave; plug->gen.close_slave = close_slave; plug->route_policy = route_policy; plug->ttable = ttable; plug->tt_ssize = tt_ssize; plug->tt_cused = tt_cused; plug->tt_sused = tt_sused;  err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode); if (err < 0) {  free(plug);  return err; } pcm->ops = &snd_pcm_plug_ops; pcm->fast_ops = slave->fast_ops; pcm->fast_op_arg = slave->fast_op_arg; // 将plug赋值到pcm的private_data pcm->private_data = plug; pcm->poll_fd = slave->poll_fd; pcm->poll_events = slave->poll_events; pcm->mmap_shadow = 1; pcm->monotonic = slave->monotonic; snd_pcm_link_hw_ptr(pcm, slave); snd_pcm_link_appl_ptr(pcm, slave); *pcmp = pcm; return 0;}


 

小结一下。
aplay传入的snd_pcm_t指针地址在snd_pcm_plug_open中被赋值
其中:

 pcm->ops = &snd_pcm_plug_ops; pcm->fast_ops = slave->fast_ops; pcm->private_data = plug; plug->gen.slave = plug->req_slave = slave;



slave在snd_pcm_dmix_open中被赋值:

 pcm->ops = &snd_pcm_dmix_ops; pcm->fast_ops = &snd_pcm_dmix_fast_ops; pcm->private_data = dmix;  dmix->spcm = spcm;



spcm在snd_pcm_hw_open_fd中被赋值:

 pcm->ops = &snd_pcm_hw_ops; pcm->fast_ops = &snd_pcm_hw_fast_ops; pcm->private_data = hw; pcm->poll_fd = fd; hw->fd = fd;



其中fd是打开的设备文件句柄。

结构体的赋值已经基本清晰。
下面来看看snd_pcm_hw_params的函数调用关系:

                                                                       snd_pcm_hw_params
                                                                          /                         \
                                                                         /                           \
                                          _snd_pcm_hw_params               snd_pcm_prepare
                                                                   /
                                                                  /
                                        snd_pcm_plug_hw_params
                                                    /                        \
                                                   /                          \
                   snd_pcm_plug_insert_plugins        _snd_pcm_hw_params
                                                   /                                       /
                                                  /                                       /
                     snd_pcm_plug_change_rate        snd_pcm_rate_hw_params
                                                /                                           /
                                               /                                           /
                            snd_pcm_rate_open        snd_pcm_hw_params_slave
                                                                                            /
                                                                                           /
                                                                  snd_pcm_generic_hw_params
                                                                                            /
                                                                                           /
                                                                    _snd_pcm_hw_params
                                                                                        /
                                                                                       /
                                                                   snd_pcm_direct_hw_params
               
开发linux audio driver的同学应该很熟悉hw:x,x设备。
可以aplay -D hw:x,x xxx.wav来指定使用hw:x,x设备播放音频。
这样指定之后,alsa lib将不会对音频进行处理,也就是说除了hw之外,其他的plug都不会被使用。
在open的时候,只调用了_snd_pcm_hw_open。
在set params时,也只调用了snd_pcm_hw_hw_params。
这样的话,声音数据直接扔给了audio driver,alsa lib中不会对采样率,声道,格式等进行处理。

0 0