首页 > 技术文章 > ALSA lib open分析

wen123456 2020-11-26 16:44 原文

参考:https://blog.csdn.net/haohenghai/article/details/24255451

alsa-lib如何解析asound.conf

https://www.xuebuyuan.com/1043778.html
一.打开代码流程分析
无论是在录音还是在放音,都要打开一个PCM流,具体对应的函数原型为:
 int snd_pcm_open(snd_pcm_t **pcmp, const char *name,  snd_pcm_stream_t stream, int mode);
先解一下各个参数的函义:
  • pcmp: 是带回的pcm流
  • name是要找开的流即放音还是录音,放音对应AndroidPlayback_Speaker_normal,录音对应AndroidCapture。放音的字符串会因为当前使用设备和设备状况不一样而有所不同,不同之处在下划线后面那部分。录音则因为大多数板子成型后,都只有一个录音设备或者接口,因此录音串对应的就是AndroidCapture。对于linux就是放音的设备,比如hw:0,2
  • stream对应流的类型,是放音(0)还是录音(1).
  • mode是指设备打开模式,阻塞或非阻塞。
  •  
 
snd_pcm_open
    //this function increases a reference counter so that the obtained tree won't be deleted until unreferenced by #snd_config_unref.
    //这个函数增加了一个引用计数器,这样得到的树在未被引用之前不会被删除# snd_config_unref。
    snd_config_update_ref(&top);
    snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0);
    if (snd_config_get_string(pcm_conf, &str) >= 0) //获取配置节点的字符串
        snd_pcm_open_noupdate(pcmp, root, str, stream, mode, hop + 1);
    else
        snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode); //单独分析1
        // 此函数将在配置树中查找对应的结点,然后将查找到的结点复制一份到pcm_conf中
        snd_config_search_definition(root, "pcm", name, &pcm_conf);
    //Decreases a reference counter of the given config tree, and eventually deletes the tree if all references are gone. This is the counterpart of #snd_config_unref.
    //减少给定配置树的引用计数器,最终如果所有引用都已消失,则删除树。这是#snd_config_unref的对等项。
    snd_config_unref(top);
 
 
单独分析1:
static const char *const build_in_pcms[] = {
 "adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat",
 "linear", "meter", "mulaw", "multi", "null", "empty", "plug", "rate", "route", "share",
 "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul",
 NULL
};
 
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)
    //Searches for a node in a configuration tree.
     * \code
 * config {
 *      a   42                                 # "a"
 *      b {                                     # "b"
 *             c   "cee"                     # "b.c"
 *              d {                             # "b.d"
 *                     e   2.71828        # "b.d.e"
 *                 }
 *         }
 * }
 * \endcode
    snd_config_search(pcm_conf, "type", &conf); //搜索config里面type的节点
    snd_config_get_id(conf, &id);  //获取ID
    snd_config_get_string(conf, &str); //获取字符串
    snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf); //搜索pcm_type
        snd_config_for_each(i, next, type_conf)
            snd_config_t *n = snd_config_iterator_entry(i);
            if (snd_config_get_id(n, &id) < 0) //看看是否有comment和open的字符
            if (strcmp(id, "comment") == 0)
            if (strcmp(id, "open") == 0)
    if (!open_name) //如果没有指定打开的设备
        buf = malloc(strlen(str) + 32); //分配空间
        open_name = buf;
        sprintf(buf, "_snd_pcm_%s_open", str); //这里是这个是str是前面type获得的
    if (!lib) ////如果前面没有指定lib
        const char *const *build_in = build_in_pcms; //赋值字符串数组build_in_pcms
        while (*build_in) //如果存在
            if (!strcmp(*build_in, str))  //如果与字符串数组中的字符相等就跳出
                break;
             build_in++;
        if (*build_in == NULL)
            buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32); //分配空间
            lib = buf1;
            sprintf(buf1, "%s/libasound_module_pcm_%s.so", ALSA_PLUGIN_DIR, str); //赋值plugin的so的路径
            //加载相关的so
            open_func = snd_dlobj_cache_get(lib, open_name, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION), 1);
               //打开动态库,如果是默认的plugin就打开这个/usr/lib64/libasound.so.2,否则就打开额外添加的/usr/lib/alsa-lib/libasound_module_pcm_pmaplug.so的plugin库
                dlobj = snd_dlopen(lib, RTLD_NOW);
                func = snd_dlsym(dlobj, name, version);  //解析来自动态库的符号,找到函数的地址,如果有的符号没有就会报错
                    return dlsym(handle, name);
                c->lib = lib ? strdup(lib) : NULL;
                c->name = strdup(name);
                list_add_tail(&c->list, &pcm_dlobj_list); //把找到的函数加入到pcm_dlobj_list这个链表
            if (open_func) //如果加载成功
                //打开这个plugin,可能调用的函数:_snd_pcm_asym_open, _snd_pcm_plug_open,等,还有执行SND_PCM_PLUGIN_DEFINE_FUNC,这些都是从so库里面得到的,获取到函数的地址
                open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);
                if ((*pcmp)->open_func)
                    snd_dlobj_cache_put(open_func); //释放解析动态库的缓存
                else 
                    (*pcmp)->open_func = open_func; //赋值
    if (err >= 0)
        snd_config_search(pcm_root, "defaults.pcm.compat", &tmp); //搜索defaults.pcm.compat这个节点
        if (err >= 0)
            if (snd_config_get_integer(tmp, &i) >= 0)
                (*pcmp)->compat = 1;
        char *str = getenv("LIBASOUND_COMPAT");
        if (str && *str) //如果环境变量有配置LIBASOUND_COMPAT,置1
            (*pcmp)->compat = 1;
        snd_config_search(pcm_root, "defaults.pcm.minperiodtime", &tmp); //获取defaults.pcm.minperiodtime
            snd_config_get_integer(tmp, &(*pcmp)->minperiodtime); //最小周期时间
 
 
 
二.实例分析
1.asound.conf内容
pcm.7input {
    type plug
    slave {
 pcm "hw:0,2"
 format S16_LE
 rate 16000
    }
}
pcm.pma {
   type pmaplug
   slave {
      pcm 7input
   }
   input_channels 8
   loopback 1
   local 1
}
pcm.speech_route {
    type route
    slave.pcm pma
    slave.channels 8
    ttable{
        0.0 1
        0.1 1
        0.2 1
        0.3 1
 0.4 1
 0.5 1
 0.6 1
 0.7 1
    }
}
pcm.speech {
    type asym
    capture.pcm speech_route
    playback.pcm default
}
 
2.添加打印后的log
第一步
ALSA lib pcm.c:2567:(snd_pcm_open_conf) cxw conf id =type, str = asym
ALSA lib pcm.c:2613:(snd_pcm_open_conf) open_name = _snd_pcm_asym_open  //获取到type asym
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = asym
............................................
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = asym
ALSA lib pcm.c:2637:(snd_pcm_open_conf) open_name lib = (null), open_name = 428883088
ALSA lib pcm.c:2641:(snd_pcm_open_conf) open_name name = speech
ALSA lib pcm_asym.c:84:(_snd_pcm_asym_open) open_name _snd_pcm_asym_open //调用到_snd_pcm_asym_open 函数
第二步
ALSA lib pcm.c:2567:(snd_pcm_open_conf) cxw conf id =type, str = route
ALSA lib pcm.c:2613:(snd_pcm_open_conf) open_name = _snd_pcm_route_open //获取到type route
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = route
................................
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = route
ALSA lib pcm.c:2637:(snd_pcm_open_conf) open_name lib = (null), open_name = 428883248
ALSA lib pcm.c:2641:(snd_pcm_open_conf) open_name name = speech_route
ALSA lib pcm_route.c:1293:(_snd_pcm_route_open) open_name _snd_pcm_route_open  //调用到_snd_pcm_route_open  函数
第三步
ALSA lib pcm.c:2567:(snd_pcm_open_conf) cxw conf id =type, str = pmaplug
ALSA lib pcm.c:2613:(snd_pcm_open_conf) open_name = _snd_pcm_pmaplug_open //获取到type pmaplug
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = pmaplug
.................................
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = pmaplug
ALSA lib pcm.c:2631:(snd_pcm_open_conf) open_name buf1 = /usr/lib/alsa-lib/libasound_module_pcm_pmaplug.so
//加载libasound_module_pcm_pmaplug.so库,并调用SND_PCM_PLUGIN_DEFINE_FUNC
ALSA lib pcm.c:2637:(snd_pcm_open_conf) open_name lib = /usr/lib/alsa-lib/libasound_module_pcm_pmaplug.so, open_name = 428882960
ALSA lib pcm.c:2641:(snd_pcm_open_conf) open_name name = pma
pmaplug() name: pma stream: 1 mode: 0
snd_pcm_extplug_create
第四步
ALSA lib pcm.c:2567:(snd_pcm_open_conf) cxw conf id =type, str = plug
ALSA lib pcm.c:2613:(snd_pcm_open_conf) open_name = _snd_pcm_plug_open  //找到type plug
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = plug
...................................
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = plug
ALSA lib pcm.c:2637:(snd_pcm_open_conf) open_name lib = (null), open_name = 428956384
ALSA lib pcm.c:2641:(snd_pcm_open_conf) open_name name = 7input
第五步
ALSA lib pcm.c:2567:(snd_pcm_open_conf) cxw conf id =type, str = hw
ALSA lib pcm.c:2613:(snd_pcm_open_conf) open_name = _snd_pcm_hw_open
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = hw
..........................................
ALSA lib pcm.c:2618:(snd_pcm_open_conf) open_name str = hw
ALSA lib pcm.c:2637:(snd_pcm_open_conf) open_name lib = (null), open_name = 429049088
ALSA lib pcm.c:2641:(snd_pcm_open_conf) open_name name = hw:0,2 //调用_snd_pcm_hw_open函数
 
分析结果:
    代码介绍alsa.conf是一个递归的过程,根据type不同,调用不同的函数,逐步递归下去。
    这是我们系统中snd_pcm_open的整个流程,虽然这里创建失败但是可以让我们清晰地认识流创建过程:它是一个分层的流建立过程。asym->route->pmaplug->plug->hw(pcm) 及asym->pmaplug->plug->hw函数层次调用过程。
 
 
 
三._snd_pcm_hw_open分析
Pcm_hw.c (z:\px30_linux\external\alsa-lib-1.1.5\src\pcm)
//Creates a new hw PCM
int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,  snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
       snd_pcm_stream_t stream, int mode)
    snd_config_for_each(i, next, conf) //遍历
        n = snd_config_iterator_entry(i);
        if (snd_config_get_id(n, &id) < 0)
        if (snd_pcm_conf_generic_id(id))
        if (strcmp(id, "card") == 0) //获取card
        if (strcmp(id, "device") == 0)  //获取device
        if (strcmp(id, "subdevice") == 0)  //获取subdevice
        。。。。。。。。。。
        if (strcmp(id, "rate") == 0) //获取rate
        if (strcmp(id, "format") == 0) //获取format
        if (strcmp(id, "channels") == 0) //获取channels
        //打开相应的hw stream
        snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream,  mode | (nonblock ? SND_PCM_NONBLOCK : 0),
          0, sync_ptr_ioctl);
            snd_ctl_hw_open(&ctl, NULL, card, 0)) //单独分析1,打开控制节点
            sprintf(filename, filefmt, card, device); //pcmC%iD%ic,配置pcm节点
            fd = snd_open_device(filename, fmode); //打开stream
                fd = open(filename, fmode);
            ioctl(fd, SNDRV_PCM_IOCTL_INFO, &info) //获取信息
            return snd_pcm_hw_open_fd(pcmp, name, fd, sync_ptr_ioctl);
                hw = calloc(1, sizeof(snd_pcm_hw_t));
                hw->version = ver;
                hw->card = info.card;
                hw->device = info.device;
                hw->subdevice = info.subdevice;
                hw->fd = fd;
                /* no restriction */
                hw->format = SND_PCM_FORMAT_UNKNOWN;
                hw->rate = 0;
                hw->channels = 0;
                ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode); //创建一个snd_pcm_t结构体
                    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->tstamp_type = tstamp_type;
                map_status_and_control_data(pcm, !!sync_ptr_ioctl); //映射状态和控制数据
 
 
单独分析1
int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode)
    sprintf(filename, SNDRV_FILE_CONTROL, card); //赋值控制"controlC%i"
    fd = snd_open_device(filename, fmode); //打开
        fd = open(filename, fmode); //打开驱动节点
        ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) //控制的版本
        hw->card = card;
        hw->fd = fd;
        hw->protocol = ver;
        err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name); //生成控制相关数据结构
        ctl->ops = &snd_ctl_hw_ops;
        ctl->private_data = hw;
        ctl->poll_fd = fd;
 
 
 
四._snd_pcm_plug_open分析
int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, 
         snd_pcm_stream_t stream, int mode)
    snd_config_for_each(i, next, conf)
        if (strcmp(id, "slave") == 0) 
            slave = n;
#ifdef BUILD_PCM_PLUGIN_ROUTE //可以设置路径相关的
        if (strcmp(id, "ttable") == 0)
         if (strcmp(id, "route_policy") == 0)
#ifdef BUILD_PCM_PLUGIN_RATE // //可以设置速率相关的
        if (strcmp(id, "rate_converter") == 0)
    //设置从设备的一些配置参数,这里会从alsa.conf里面获取
    snd_pcm_slave_conf(root, slave, &sconf, 3, SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
     SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,  SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
    err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
        return snd_pcm_open_named_slave(pcmp, NULL, root, conf, stream, mode, parent_conf);
            snd_pcm_open_conf(pcmp, name, root, conf, stream, mode); //前面有分析,获取相关的alsa.conf参数
    //配置返回之后,就会到这里. 单独分析1
    snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter, route_policy, ttable, ssize, cused, sused, spcm, 1);  
 
单独分析1
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)
    plug->sformat = sformat;
    plug->schannels = schannels;
    plug->srate = srate;
    //将HW层的pcm流对象存放在PLUG层pcm流的私有数据成员中 ,slave就是那个指针
    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); //创建SND_PCM_TYPE_PLUG数据流
    pcm->ops = &snd_pcm_plug_ops;
    pcm->fast_ops = slave->fast_ops;
    pcm->fast_op_arg = slave->fast_op_arg;
    pcm->private_data = plug;
    pcm->poll_fd = slave->poll_fd;
    pcm->poll_events = slave->poll_events;
    pcm->mmap_shadow = 1;
    pcm->tstamp_type = slave->tstamp_type;
    snd_pcm_link_hw_ptr(pcm, slave);
    snd_pcm_link_appl_ptr(pcm, slave);
    *pcmp = pcm;
 
 
五._snd_pcm_pmaplug_open 分析
因为有这个两个宏
#define SND_PCM_PLUGIN_ENTRY(name)   _snd_pcm_##name##_open
SND_PCM_PLUGIN_DEFINE_FUNC(pmaplug) ->_snd_pcm_pmaplug_open
 
#define SND_PCM_PLUGIN_DEFINE_FUNC(plugin) \
int SND_PCM_PLUGIN_ENTRY(plugin) (snd_pcm_t **pcmp, const char *name,\
      snd_config_t *root, snd_config_t *conf, \
      snd_pcm_stream_t stream, int mode)
所有,下面这个就是
_snd_pcm_pmaplug_open(snd_pcm_t **pcmp, const char *name,  snd_config_t *root, snd_config_t *conf, \
      snd_pcm_stream_t stream, int mode)
    snd_config_for_each(i, next, conf) //获取配置相关
        if (strcmp(id, "slave") == 0)
            slave = n;
        (strcmp(id, "input_channels") == 0)
        。。。。。。。
    pmaplug->ext.version = SND_PCM_EXTPLUG_VERSION;
    pmaplug->ext.name = "pmaplug"; //名字
    pmaplug->ext.callback = &callbacks; //回调函数
    pmaplug->ext.private_data = pmaplug; //私有
    pmaplug->local = local; //私有的
    pmaplug->loopback = loopback;  
//私有的
    //实例化,Create an extplug instance
    snd_pcm_extplug_create(&pmaplug->ext, name, root, slave, stream, mode);  //单独分析1
    // mono out, user defined number in,设置
    snd_pcm_extplug_set_param(&pmaplug->ext, SND_PCM_EXTPLUG_HW_CHANNELS, channels);
    snd_pcm_extplug_set_slave_param(&pmaplug->ext, SND_PCM_EXTPLUG_HW_CHANNELS, channels);
    // take double in and write signed 16 bit ints out, doubles are for PMA,设置
    snd_pcm_extplug_set_param(&pmaplug->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_S16_LE);
    snd_pcm_extplug_set_slave_param(&pmaplug->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_FLOAT64_LE);
    *pcmp = pmaplug->ext.pcm;
 
 
 
 //单独分析1
int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name, 
 snd_config_t *root, snd_config_t *slave_conf,
      snd_pcm_stream_t stream, int mode)
    snd_pcm_slave_conf(root, slave_conf, &sconf, 0); 
    snd_pcm_open_slave(&spcm, root, sconf, stream, mode, NULL); //之前有分析
    ext = calloc(1, sizeof(*ext));
    ext->data = extplug;
    extplug->stream = stream;
    ext->plug.read = snd_pcm_extplug_read_areas;
    ext->plug.write = snd_pcm_extplug_write_areas;
    ext->plug.undo_read = snd_pcm_plugin_undo_read_generic;
    ext->plug.undo_write = snd_pcm_plugin_undo_write_generic;
    //将plug层的pcm流对象存放在ext->plug层pcm流的私有数据成员中 ,spcm就是那个指针
    ext->plug.gen.slave = spcm;
    ext->plug.gen.close_slave = 1;
    snd_pcm_new(&pcm, SND_PCM_TYPE_EXTPLUG, name, stream, mode); //创建一个SND_PCM_TYPE_EXTPLUG流
    extplug->pcm = pcm;
    pcm->ops = &snd_pcm_extplug_ops;
    pcm->fast_ops = &snd_pcm_plugin_fast_ops;
    pcm->private_data = ext;
    pcm->poll_fd = spcm->poll_fd;
    pcm->poll_events = spcm->poll_events;
    snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0);
    snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0);
 
 
六._snd_pcm_route_open分析
int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, 
   snd_pcm_stream_t stream, int mode)
    snd_config_for_each(i, next, conf)
        snd_config_t *n = snd_config_iterator_entry(i);
        if (strcmp(id, "slave") == 0)
            slave = n;
        if (strcmp(id, "ttable") == 0)
            tt = n;
        err = snd_pcm_slave_conf(root, slave, &sconf, 2, SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
         SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
        determine_chmap(tt, &tt_chmap); //决定通道的map
        snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
        if (tt_chmap)
            find_matching_chmap(spcm, tt_chmap, &chmap, &schannels);
        _snd_pcm_route_determine_ttable(tt, &csize, &ssize, chmap);
        _snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, schannels, chmap);
        snd_pcm_route_open(pcmp, name, sformat, schannels, ttable, ssize, cused, sused, spcm, 1); //单独分析1
 
单独分析1
int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, int schannels,
         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)
    route = calloc(1, sizeof(snd_pcm_route_t));
    snd_pcm_plugin_init(&route->plug);
    route->sformat = sformat;
    route->schannels = schannels;
    route->plug.read = snd_pcm_route_read_areas;
    route->plug.write = snd_pcm_route_write_areas;
    route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
    route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
    route->plug.gen.slave = slave; //赋值slave指针
    route->plug.gen.close_slave = close_slave;
    route->plug.init = route_chmap_init;
    snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode); //创建SND_PCM_TYPE_ROUTE的pcm
    pcm->ops = &snd_pcm_route_ops;
    pcm->fast_ops = &snd_pcm_plugin_fast_ops; //赋值操作函数
    pcm->private_data = route;
    pcm->poll_fd = slave->poll_fd;
    pcm->poll_events = slave->poll_events;
    pcm->tstamp_type = slave->tstamp_type;
    snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
    snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
    err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
 
 
七.总结
snd_pcm_open函数主要完成如下两步:
第一步:构建配置树
 第二步:创建PCM流
 
由此可见,在整个过程中创建了三种类型的pcm流对象,EXTPLUG,PLUG,HW
_snd_pcm_asym_open  -->_snd_pcm_route_open--->_snd_pcm_pmaplug_open--->_snd_pcm_plug_open--->_snd_pcm_hw_open层层递进分析配置树
_snd_pcm_asym_open  <--....................................<---_snd_pcm_plug_open<--_snd_pcm_hw_open层层回溯。
 回溯过程依次创建了HW pcm-->PLUG pcm-->EXTPLUG pcm-->ROUTE pcm及和它们区配的私有数据及相关操作函数集,而且可以通过ROUTE层的pcm流,查找到EXTPLUG ,plug,hw的pcm.它们都被寄存在上层的private_data成员中。
 
 

深入了解ALSA

https://blog.csdn.net/kickxxx/article/details/8291598

alsa-lib如何解析asound.conf

https://blog.csdn.net/MyArrow/article/details/8230231?utm_source=blogxgwz9

Why asoundrc?

https://www.alsa-project.org/main/index.php/Asoundrc

alsa lib中ttable相关学习

https://blog.csdn.net/njuitjf/article/details/14126255

ALSA分析

https://blog.csdn.net/zhang_danf/article/details/38824013

Audio模拟多声道

https://blog.csdn.net/u012769691/article/details/46729811
 
 
 
 
 
 
 
        
       
 
            
        
                    
                
 
 
 
 
 



 

Attachment List

 

推荐阅读