android alsa分析之三

 

       snd_pcm_open 总结

  
  在阅读本总结前,请认真看一下snd_pcm_open。
  本文就以树的结构来分析一下pcm流建立的过程。
        
     

  


 
       default树
 为了简便起见,下面就称第一颗树为capture树,第二颗为default树。
 int  snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)
 {
  int err;
  assert(pcmp && name);
  //刷新/system/usr/share/alsa/alsa.conf文件内容到snd_config_t结构中,构建配置树
  err = snd_config_update();
  if (err < 0)
   return err;
  //真正执行树创建的例程
  return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);
 }
 该函数主要完成如下两步:
 第一步:构建配置树
 第二步:创建PCM流
 点击录音时传入的name为AndroidCapture(其它模式类似),在第二步中根据传入的name参数在配置树中查找对应的snd_config_t结点。根据我们的配置asla-lib.conf,查找到capture树。
 下面看一下这个函数,snd_pcm_open_noupdate会调用到这里
 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);
 这个函数的功能是取下pcm_conf配置树中type孩子结点对应的字符串值,并利用该字符串构造一新的函数并调用。在我们的系统中可以简单地这样理解。下面以capture树为例说明一下。
 snd_pcm_open-->snd_pcm_open_noupdate-->snd_pcm_open_conf此时传入的pcm_conf配置树为capture树,它会取下type孩子结点的字符串str(hooks),构建函数名sprintf(open_name, "_snd_pcm_%s_open", str);之后调用函数_snd_pcm_hooks_open.下snd_pcm_open的主要流程:
 _snd_pcm_hooks_open-->_snd_pcm_empty_open--->_snd_pcm_plug_open--->_snd_pcm_hw_open
 结合capture树和default树,不难看出,后三个函数名也是用类似构造_snd_pcm_hook_open的方法生成的,之后调用。事实确实是这样的,在snd_pcm_open过程中多次调用snd_pcm_open_conf只是每次传入的pcm_conf配置树不一样而已。那么我们的default树是如何获取的呢?
 在_snd_pcm_hook_open中会查找capture树的slave孩子结点,并将slave的pcm子结点的str(default)作为新的name重新调用snd_pcm_open_noupdate.然后根据name参数在配置树中查找到default对应的结点,即default树。
 前面说过snd_pcm_open_noupdate会调用到snd_pcm_open_conf,由此不难想象到它会构造_snd_pcm_empty_open,并调用它。之后_snd_pcm_plug_open-->_snd_pcm_hw_open函数的构造也是类似的这里都不多介绍了。读者可认真看一下_snd_pcm_xxx_open,会发现它都会调用到snd_pcm_open_conf函数来加载下一层的type xxxx.就这样对default树进行逐层分析。
 _snd_pcm_hooks_open-->_snd_pcm_empty_open->_snd_pcm_plug_open->_snd_pcm_hw_open
 上面是我给大家介绍的重点之一。
 此流程执行完,也要逐层返回,在_snd_pcm_hooks_open/_snd_pcm_plug_open/_snd_pcm_hw_open三层分别还会创建三种类型的pcm流对象和它的私有数据成员及和它对应的函数集。
 下面就从里到外顺序介绍一下相关代码,先看一下hw层
 ......
 //pcm_hw.c 创建类型为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;
 }
 //HW层的函数集
 pcm->ops = &snd_pcm_hw_ops;
 pcm->fast_ops = &snd_pcm_hw_fast_ops;
 //snd_pcm_hw_t结构hw作为私有数据成员,存储一些信息。
 pcm->private_data = hw;
 //打开/dev/snd/pcmC0D0c
 pcm->poll_fd = fd;
 ....
 返回plug层
 //pcm_plug.c
 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;
 //将HW层的pcm流对象存放在PLUG层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; 
 //创建SND_PCM_TYPE_PLUG类型的pcm流对象
 err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
 if (err < 0) {
  free(plug);
  return err;
 }
 //PLUG层函数集
 pcm->ops = &snd_pcm_plug_ops;
 pcm->fast_ops = slave->fast_ops;
 pcm->fast_op_arg = slave->fast_op_arg;
 //plug关联到私有成员中
 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;

 最后再来看一下HOOKS层
 //pcm_hooks.c
 int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
{
 snd_pcm_t *pcm;
 snd_pcm_hooks_t *h;
 unsigned int k;
 int err;
 assert(pcmp && slave);
 //分配私有成员空间
 h = calloc(1, sizeof(snd_pcm_hooks_t));
 if (!h)
  return -ENOMEM;
 //寄存PLUG层的pcm流对象
 h->gen.slave = slave;
 h->gen.close_slave = close_slave;
 for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
  INIT_LIST_HEAD(&h->hooks[k]);
 }
 //SND_PCM_TYPE_HOOKS类型的pcm流对象创建
 err = snd_pcm_new(&pcm, SND_PCM_TYPE_HOOKS, name, slave->stream, slave->mode);
 if (err < 0) {
  free(h);
  return err;
 }
 //HOOKS层函数集
 pcm->ops = &snd_pcm_hooks_ops;
 pcm->fast_ops = &snd_pcm_hooks_fast_ops;
 //关联私有数据
 pcm->private_data = h;
 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;
}
 由此可见,在整个过程中创建了三种类型的pcm流对象,HOOKS,PLUG,HW
 _snd_pcm_hooks_open-->_snd_pcm_empty_open--->_snd_pcm_plug_open--->_snd_pcm_hw_open层层递进分析配置树
 _snd_pcm_hooks_open<--....................................<---_snd_pcm_plug_open<--_snd_pcm_hw_open层层回溯。
 回溯过程依次创建了HW pcm-->PLUG pcm-->HOOKS pcm及和它们区配的私有数据及相关操作函数集,而且可以通过HOOKS层的pcm流,查找到plug,hw的pcm.它们都被寄存在上层的private_data成员中。如下图
 
 
 至此返回_snd_pcm_hooks_open,下面这个函数还会对capture树的hooks孩子结点分析。
 static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_t *conf)
 它类似snd_pcm_open_conf,它会调用_snd_pcm_hook_ctl_elems_install-->snd_ctl_open打开对应的/dev/snd/ControlC0(snd_ctl_open和snd_pcm_open类似都是通过传入字符串查找配置树,然后分析找开设备之类操作),执行成功能会带回一个snd_sctl_t结构类型对象。
 由此可以PCM流打开需要两步:snd_pcm_open /dev/snd/pcmC0D0c 的打开和snd_ctl_open /dev/snd/ControlC0的打开,两个设备文件。
 之后_snd_pcm_hook_ctl_elems_install->snd_sctl_build,但是根据我们的配置在这里会返回错误信息(snd_sctl_build会添加hook_args下面所有的参数信息并生成一个新的snd_sctl_t对象(add_elem),就是在添加参数信息时我们的配置有问题,所以出错返回)。错误发生,会释放HOOKS层的pcm流对象,级联PLUG和HW层。最终snd_pcm_open会返回错误信息。pcm流创建失败。
 这是我们系统中snd_pcm_open的整个流程,虽然这里创建失败但是可以让我们清晰地认识流创建过程:它是一个分层的流建立过程。hooks->plug->hw(pcm) 及hooks->empty->plug->hw函数层次调用过程。
 这个层次结构就是我给大家介绍的第二个要点,特别是多种pcm流类型,及它们相互引用和各自的私有数据成员及对应的函数操作集。有了这个认识,我们才能在pcm->fast_ops->read(..)时知道调用的是哪一层的函数集。
 虽然这里创建PCM流失败,但是我们的系统会有另一种方案重新创建default pcm流。可以参考我们系统中流的创建过程。
 
 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值