现在的位置: 首页 > 综合 > 正文

alsa sample rate跟踪 <2>

2013年01月11日 ⁄ 综合 ⁄ 共 8336字 ⁄ 字号 评论关闭

alsa sample rate跟踪 <2>

接上篇,下面要调查:
1、snd_pcm_hw_hw_params,snd_pcm_plug_hw_params,snd_pcm_rate_hw_params,snd_pcm_generic_hw_params和snd_pcm_direct_hw_params是如何被调用的。
2、以及snd_pcm_hw_params和几处单独的_snd_pcm_hw_params是如何被调用的。

先看看第一话题。
snd_pcm_hw_hw_params:
经过前面分析可知,snd_pcm_hw_hw_params赋值给了snd_pcm_hw_ops结构体。
函数snd_pcm_hw_open_fd中使用了snd_pcm_hw_ops结构体。

/**
 * \brief Creates a new hw PCM
 * \param pcmp Returns created PCM handle
 * \param name Name of PCM
 * \param fd File descriptor
 * \param mmap_emulation Obsoleted parameter
 * \param sync_ptr_ioctl Boolean flag for sync_ptr ioctl
 * \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_hw_open_fd(snd_pcm_t **pcmp, const char *name,
         int fd, int mmap_emulation ATTRIBUTE_UNUSED,
         int sync_ptr_ioctl)
{
...
 snd_pcm_t *pcm = NULL;
...
 pcm->ops = &snd_pcm_hw_ops;
...
 *pcmp = pcm;
 return 0;
}

 

搜索alsa lib代码,发现有两处调用了snd_pcm_hw_open_fd函数,函数snd_pcm_hw_open和函数snd_pcm_direct_open_secondary_client。
经确认,在我的测试过程中,是函数snd_pcm_hw_open调用了函数snd_pcm_hw_open_fd。
按照以前对alsa lib的理解,snd_pcm_XX_open应该在snd_pcm_open_conf中被调用,snd_pcm_hw_open当然也不例外。
有以下调用链:
snd_pcm_open -》 snd_pcm_open_noupdate -》 snd_pcm_open_conf
snd_pcm_open是在aplay中被调用的。

snd_pcm_plug_hw_params:
snd_pcm_plug_hw_params被赋值给了结构体snd_pcm_plug_ops。

static const snd_pcm_ops_t snd_pcm_plug_ops = {
...
 .hw_params = snd_pcm_plug_hw_params,
...
}

函数snd_pcm_plug_open中使用了结构体snd_pcm_plug_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;
...
 pcm->ops = &snd_pcm_plug_ops;
...
 *pcmp = pcm;

 return 0;
}

比snd_pcm_hw_hw_params简单些,少了一层。

函数snd_pcm_plug_open也是在函数snd_pcm_open_conf中被调用。

snd_pcm_rate_hw_params&snd_pcm_generic_hw_params:
snd_pcm_rate_hw_params与snd_pcm_plug_hw_params类似。
被赋值给结构体snd_pcm_rate_ops,函数snd_pcm_rate_open中将结构体赋值给pcm。

看了下snd_pcm_rate_hw_params的实现,发现snd_pcm_generic_hw_params的调用也被包含其中:

static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
 snd_pcm_rate_t *rate = pcm->private_data;
 snd_pcm_t *slave = rate->gen.slave;
 snd_pcm_rate_side_info_t *sinfo, *cinfo;
 unsigned int channels, cwidth, swidth, chn;
 int err = snd_pcm_hw_params_slave(pcm, params,
       snd_pcm_rate_hw_refine_cchange,
       snd_pcm_rate_hw_refine_sprepare,
       snd_pcm_rate_hw_refine_schange,
       snd_pcm_generic_hw_params);
...
}

snd_pcm_hw_params_slave的实现:

int snd_pcm_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
       int (*cchange)(snd_pcm_t *pcm,
        snd_pcm_hw_params_t *params,
        snd_pcm_hw_params_t *sparams),
       int (*sprepare)(snd_pcm_t *pcm,
         snd_pcm_hw_params_t *params),
       int (*schange)(snd_pcm_t *pcm,
        snd_pcm_hw_params_t *params,
        snd_pcm_hw_params_t *sparams),
       int (*sparams)(snd_pcm_t *pcm,
        snd_pcm_hw_params_t *sparams))

{
 snd_pcm_hw_params_t slave_params;
 int err;
 err = sprepare(pcm, &slave_params);
 assert(err >= 0);
 err = schange(pcm, params, &slave_params);
 assert(err >= 0);
 err = sparams(pcm, &slave_params);
 if (err < 0)
  cchange(pcm, params, &slave_params);
 return err;
}

 

snd_pcm_direct_hw_params:
使用snd_pcm_direct_hw_params的地方比较多,它分别被赋值给了snd_pcm_dmix_ops,snd_pcm_dshare_ops和snd_pcm_dsnoop_ops三个结构体。
经过分析,我的测试中使用的是snd_pcm_dmix_ops。
函数snd_pcm_dmix_open中将snd_pcm_dmix_ops赋值给了pcm->ops:
 pcm->ops = &snd_pcm_dmix_ops;
snd_pcm_dmix_open也是在 函数snd_pcm_open_conf中被调用。

通过在函数snd_pcm_open_conf中加log发现,我的测试过程中依次调用了下面三个open函数:
_snd_pcm_plug_open
_snd_pcm_dmix_open
_snd_pcm_hw_open
上面三个函数又分别调用了以下三个函数:
snd_pcm_plug_open
snd_pcm_dmix_open
snd_pcm_hw_open
这三个函数之所以被调用,是因为我们的asound.conf中有以下内容:

pcm.dshare_1 {
    type dmix
    ipc_key 2048
    slave {             
        pcm "hw:0,0"
        channels 2
        format S16_LE
  rate 48000
    }
}
pcm.mydev {
    type plug
    slave {
        pcm "dshare_1"
    }
}

因为有plug,所以snd_pcm_plug_open被调用。
因为有dmix,所以snd_pcm_dmix_open被调用。
播放声音,最终要打开硬件的声卡设备,所以snd_pcm_hw_open最终被调用。

接着看第二个问题:
通过log发现,snd_pcm_hw_params被调用过两次:
第一次调用在:pcm_direct.c中的snd_pcm_direct_initialize_slave函数。
第二次调用在:aplay.c中的set_params函数。
第二次调用是应用程序主动调用,就不多看了。
看看第一次。

有个插曲,开始打了点log有点迷惑,有以下log:
snd_pcm_open_conf - open_name=_snd_pcm_plug_open
snd_pcm_open_conf - open_name=_snd_pcm_dmix_open
snd_pcm_open_conf - open_name=_snd_pcm_hw_open
...
snd_pcm_dmix_open(pcm_dmix.c) - calling snd_pcm_direct_initialize_slave
snd_pcm_direct_initialize_slave(pcm_direct.c) - calling snd_pcm_hw_params
开始的理解是某个地方通过层层调用,调用到snd_pcm_open_conf,有依次调用了上述三个open函数。
这样就产生了两个疑问:
1、如果是依次调用,snd_pcm_dmix_open中的log为什么会出现在snd_pcm_open_conf - open_name=_snd_pcm_hw_open之后?
2、从snd_pcm_open开始,到snd_pcm_open_conf,怎么也找不到while或for,依次调用是怎么产生的?
后来才明白,不是依次调用,而是层层调用。
看看调用关系:(只看我们关心的函数)

                        snd_pcm_open
                         /                   \
                        /                     \
      snd_config_update      snd_pcm_open_noupdate
                                                    \
                                                     \
                                        snd_pcm_open_conf
                                                    \
                                                     \
                                      _snd_pcm_plug_open
                                            /                 \
                                           /                    \
          snd_pcm_open_slave            snd_pcm_plug_open
                      /
                     /
        snd_pcm_open_named_slave
                 /                                     \
               /                                        \
     snd_pcm_open_noupdate      snd_pcm_open_conf
                                                             \
                                                              \
                                                 _snd_pcm_dmix_open
                                                                 \
                                                                  \
                                                    snd_pcm_dmix_open
                                                            /                  \
                                                           /                    \
                   snd_pcm_direct_initialize_slave     snd_pcm_open_slave
                         /                         /             \                                \
                        /                         /               \                                \
 snd_pcm_hw_params_any     /  snd_pcm_hw_params       snd_pcm_open_named_slave
                                                /                                                         /                     \
                                               /                                                         /                       \
 snd_pcm_hw_params_set_rate_near             snd_pcm_open_noupdate    snd_pcm_open_conf
                                                                                                                                    \
                                                                                                                                     \
                                                                                                                      _snd_pcm_hw_open
                                                                                                                           /                   \
                                                                                                                          /                     \
                                                                                                    snd_open_device      snd_pcm_hw_open_fd
              
              
snd_pcm_hw_params调用的地方找到了。
从上面的log可知,第一次调用snd_pcm_hw_params时,sample rate为48000,这个48000是怎么来的呢?
snd_pcm_hw_params的params是函数snd_pcm_direct_initialize_slave传入的参数hw_params。
中函数snd_pcm_direct_initialize_slave中,首先调用snd_pcm_hw_params_any将hw_params的rate初始化成8000。
然后调用snd_pcm_hw_params_set_rate_near将hw_params的rate中的rate设置为与slave params一致。
slave params既是snd_pcm_direct_initialize_slave的传入参数params。

要寻找48000,那就顺着snd_pcm_direct_initialize_slave往上找。
终于找到了,在函数_snd_pcm_dmix_open中定义了slave_params结构体,并对其进行初始化。
其中将rate初始化为48000,然后调用snd_pcm_dmix_open时将slave params传入。
真相大白。

抱歉!评论已关闭.