linux alsa-lib snd_pcm_open函数源码分析(一)

访问原版内容,可直接到博客linux alsa-lib snd_pcm_open函数源码分析(一)

系列文章其他部分:

linux alsa-lib snd_pcm_open函数源码分析(二)

linux alsa-lib snd_pcm_open函数源码分析(三)

linux alsa-lib snd_pcm_open函数源码分析(四)

linux alsa-lib snd_pcm_open函数源码分析(五)

linux alsa-lib snd_pcm_open函数源码分析(六)

snd_pcm_open通常是接触alsa-lib的第一个api,也通常是使用alsa进行录音或播放的第一步。 正如名字中表示的一样,snd_pcm_open用来打开一个pcm音频设备,并得到这个音频设备的句柄, 即便用户使用了alsa的插件,使用时也同样是使用此接口,非常方便。但是这种使用上的方便是以snd_pcm_open及其复杂的实现为代价的。 这篇笔记的目的就是尽可能详细的分析这个函数到底做了什么工作,以及这些工作到底是怎么实现的。

1.版本信息

  • alsa-lib-1.1.5
  • alsa-plugins-1.1.5
  • linux-kernel-4.4.167

2.函数原型

snd_pcm_open实现位于alsa-lib/src/pcm/pcm.c中。 从函数实现上主要有两个功能,第一个是更新配置文件,第二个是打开设备。 这两个函数过程都很复杂,后面我们会继续详细解释。

cpp 复制代码
/*
 * \brief Opens a PCM
 * \param pcmp 返回pcm句柄
 * \param name 要打开的pcm设备的名字
 * \param stream 想要的stream类型
 * \param mode 打开模式 (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC)
 * \return 0 表示成功,否则返回一个负的错误码
 */
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
         snd_pcm_stream_t stream, int mode)
{
    snd_config_t *top;
    int err;

    assert(pcmp && name);
    err = snd_config_update_ref(&top);
    if (err < 0)
        return err;
    err = snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0);
    snd_config_unref(top);
    return err;
}

3.使用示例

下面是一段播放音频的代码,大部分来自于alsa-utils中的aplay.c文件,为了方便了解使用流程对其做了精简, 此示例仅仅为了说明snd_pcm_open的位置,无法直接编译运行。

4.代码分析

下面是对snd_pcm_open函数的分析过程,函数主要实现了两个工作:更新配置文件及打开pcm设备。 我们按照这两部分分别进行分析。

4.1 snd_config_update_ref

函数的目的是更新snd_config配置,与snd_config_update_r功能类似,主要区别是此函数会增加引用计数, 所以在引用计数为0前获取到配置树将永远不会被删除。同时由于函数使用了锁,所以函数是线程安全的。

注意这里的参数top,是个二级指针。在snd_pcm_open中传下来的是snd_config_t *top;中top的地址。

关键函数snd_config_update_r的详细分析参考inux alsa-lib snd_pcm_open函数详细分析(二)

cpp 复制代码
/* top为出参 */
int snd_config_update_ref(snd_config_t **top)
{
    int err;

    if (top)
        *top = NULL;
    snd_config_lock();  /*加锁保证线程安全*/
    /* 主要功能实现在此,后续文章会继续分析 */
    err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL);
    if (err >= 0) {
        if (snd_config) {
            if (top) {
                snd_config->refcount++; /*增加引用计数*/
                *top = snd_config;      /*最终返回结果*/
            }
        } else {
            err = -ENODEV;
        }
    }
    snd_config_unlock();
    return err;
}
4.2 snd_pcm_open_noupdate

函数的目的是打开pcm设备,所要打开的具体设备需要依赖上面打开的设备树, 函数接受传入的设备名称,解析名称并在设备树中查找需要打开的设备, 如果配置中有使用插件,函数还需要解析插件,最终打开硬件设备。 注意snd_pcm_open函数最终返回的句柄pcmp其实就是此函数的返回的。

此函数会在后面文章继续分析。

cpp 复制代码
static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
                 const char *name, snd_pcm_stream_t stream,
                 int mode, int hop)
{
    int err;
    snd_config_t *pcm_conf;
    const char *str;

    err = snd_config_search_definition(root, "pcm", name, &pcm_conf);
    if (err < 0) {
        SNDERR("Unknown PCM %s", name);
        return err;
    }
    if (snd_config_get_string(pcm_conf, &str) >= 0)
        err = snd_pcm_open_noupdate(pcmp, root, str, stream, mode,
                        hop + 1);
    else {
        snd_config_set_hop(pcm_conf, hop);
        err = snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);
    }
    snd_config_delete(pcm_conf);
    return err;
}

各子函数的详细介绍参考本系列其他文章

相关推荐
枯无穷肉4 分钟前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
ldinvicible10 分钟前
How to run Flutter on an Embedded Device
linux
不过四级不改名67720 分钟前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式大圣42 分钟前
单片机UDP数据透传
单片机·嵌入式硬件·udp
YRr YRr1 小时前
解决Ubuntu 20.04上编译OpenCV 3.2时遇到的stdlib.h缺失错误
linux·opencv·ubuntu
认真学习的小雅兰.1 小时前
如何在Ubuntu上利用Docker和Cpolar实现Excalidraw公网访问高效绘图——“cpolar内网穿透”
linux·ubuntu·docker
云山工作室1 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费1 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
zhou周大哥1 小时前
linux 安装 ffmpeg 视频转换
linux·运维·服务器
不想起昵称9292 小时前
Linux SHELL脚本中的变量与运算
linux