pcm设备相关代码解析

这篇具有很好参考价值的文章主要介绍了pcm设备相关代码解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

注:这部分主要是tinyplay和tinycap涉及到

结构体

先把涉及到的数据结构列出:

struct pcm {
    int fd;
    unsigned int flags;
    int running:1;
    int prepared:1;
    int underruns;
    unsigned int buffer_size;
    unsigned int boundary;
    char error[PCM_ERROR_MAX];
    struct pcm_config config;
    struct snd_pcm_mmap_status *mmap_status;
    struct snd_pcm_mmap_control *mmap_control;
    struct snd_pcm_sync_ptr *sync_ptr;
    void *mmap_buffer;
    unsigned int noirq_frames_per_msec;
    int wait_for_avail_min;
    unsigned int subdevice;

    struct pcm_ops *ops;
    void *data;
    void *snd_node;
};

/* Configuration for a stream */
struct pcm_config {
    unsigned int channels;
    unsigned int rate;
    unsigned int period_size;
    unsigned int period_count;
    enum pcm_format format;

    /* Values to use for the ALSA start, stop and silence thresholds, and
     * silence size.  Setting any one of these values to 0 will cause the
     * default tinyalsa values to be used instead.
     * Tinyalsa defaults are as follows.
     *
     * start_threshold   : period_count * period_size
     * stop_threshold    : period_count * period_size
     * silence_threshold : 0
     * silence_size      : 0
     */
    unsigned int start_threshold;
    unsigned int stop_threshold;
    unsigned int silence_threshold;
    unsigned int silence_size;

    /* Minimum number of frames available before pcm_mmap_write() will actually
     * write into the kernel buffer. Only used if the stream is opened in mmap mode
     * (pcm_open() called with PCM_MMAP flag set).   Use 0 for default.
     */
    int avail_min;
};

/* pcm parameters */
enum pcm_param
{
    /* mask parameters */
    PCM_PARAM_ACCESS,
    PCM_PARAM_FORMAT,
    PCM_PARAM_SUBFORMAT,
    /* interval parameters */
    PCM_PARAM_SAMPLE_BITS,
    PCM_PARAM_FRAME_BITS,
    PCM_PARAM_CHANNELS,
    PCM_PARAM_RATE,
    PCM_PARAM_PERIOD_TIME,
    PCM_PARAM_PERIOD_SIZE,
    PCM_PARAM_PERIOD_BYTES,
    PCM_PARAM_PERIODS,
    PCM_PARAM_BUFFER_TIME,
    PCM_PARAM_BUFFER_SIZE,
    PCM_PARAM_BUFFER_BYTES,
    PCM_PARAM_TICK_TIME,
};

pcm_open()

struct pcm *pcm_open(unsigned int card, unsigned int device,
					 unsigned int flags, struct pcm_config *config)
{
    struct pcm *pcm;
    struct snd_pcm_info info;
    struct snd_pcm_hw_params params;
    struct snd_pcm_sw_params sparams;
    int rc, pcm_type;

	...
}
  1. 判断config存在 --- if (!config)

  2. 为pcm分配空间 --- calloc()

  3. 拿到pcm设备的节点 --- pcm->snd_node = snd_utils_get_dev_node()

  4. 拿到pcm类型 --- pcm_type = snd_utils_get_node_type()

  5. 打开设备 --- pcm->fd = pcm->ops->open(card, device, flags, &pcm->data, pcm->snd_node)

  6. 拿到相关信息 --- pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_INFO, &info)

  7. 初始化参数信息 --- param_init(&params)

  8. 设置参数 --- param_set_mask() param_set_int()

  9. 写入参数 --- pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_HW_PARAMS, &params)

  10. 读出设置好后的参数,拿到映射的内存地址 ---

    /* get our refined hw_params */
    config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
    config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
    pcm->buffer_size = config->period_count * config->period_size;
    ...
    pcm->mmap_buffer = pcm->ops->mmap(pcm->data, NULL,
    			pcm_frames_to_bytes(pcm, pcm->buffer_size),
    			PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, 0);
  11. 根据config的值设置sparam以及pcm->config里的相关参数

  12. 写入参数 --- pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)

  13. 检测映射状态 --- rc = pcm_hw_mmap_status(pcm);

  14. 设置pcm状态并返回 --- pcm->underruns = 0; return pcm;

注:补充一下第3和4点,其实3里面涉及到了一个动态库"libsndcardparser.so",但是这个库一般默认不存在,它属于一个插件框架,所以3返回为NULL,这也导致了4返回SND_NODE_TYPE_HW,最后导致ops = &hw_ops;

下面再解析一下ops的相关内容

struct pcm_ops hw_ops = {
    .open = pcm_hw_open,
    .close = pcm_hw_close,
    .ioctl = pcm_hw_ioctl,
    .mmap = pcm_hw_mmap,
    .munmap = pcm_hw_munmap,
    .poll = pcm_hw_poll,
};
struct pcm_hw_data {
    unsigned int card;
    unsigned int device;
    unsigned int fd;
    void *snd_node;
};

来看看pcm_ops_open()

static int pcm_hw_open(unsigned int card, unsigned int device,
                unsigned int flags, void **data,
                __attribute__((unused)) void *node)
{
    struct pcm_hw_data *hw_data;
    char fn[256];
    int fd;
    ...
} 
  1. 为pcm_hw_data分配内存 --- calloc()

  2. 拼凑出节点名称并打开 --- snprintf(fn, ...) open(fn, ...)

  3. 设置该文件读写方式为阻塞方式 --- fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK)

  4. 为hw_data赋值之后返回

hw_data->snd_node = node;
hw_data->card = card;
hw_data->device = device;
hw_data->fd = fd;
*data = hw_data;
return fd;

pcm_hw_ioctl()则是通过调用ioctl()完成相关操作,不展开讨论

pcm_write()

int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
{
	struct snd_xferi x;				
	...
}

struct snd_xferi {
        snd_pcm_sframes_t result;
        void __user *buf;
        snd_pcm_uframes_t frames;
};
  1. 判断标志位 --- if (pcm->flags & PCM_IN)

  2. 为x赋值 ---

    x.buf = (void*)data;
    x.frames = count / (pcm->config.channels *
                        pcm_format_to_bits(pcm->config.format) / 8);
  3. 进入无限for循环,这里也就是写数据、判断是否成功及改变标志位 ---

    for (;;) {
        if (!pcm->running) {
            int prepare_error = pcm_prepare(pcm);
            if (prepare_error)
                return prepare_error;
            if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
                return oops(pcm, errno, "cannot write initial data");
            pcm->running = 1;
            return 0;
        }
        if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
            pcm->prepared = 0;
            pcm->running = 0;
            if (errno == EPIPE) {
                /* we failed to make our window -- try to restart if we are
                 * allowed to do so.  Otherwise, simply allow the EPIPE error to
                 * propagate up to the app level */
                pcm->underruns++;
                if (pcm->flags & PCM_NORESTART)
                    return -EPIPE;
                continue;
            }
            return oops(pcm, errno, "cannot write stream data");
        }
        return 0;
    }

pcm_read()

pcm_read()和read_write()十分类似,直接列出pcm_read()里面的循环部分:文章来源地址https://www.toymoban.com/news/detail-402537.html

for (;;) {
    if (!pcm->running) {
        if (pcm_start(pcm) < 0) {
            fprintf(stderr, "start error");
            return -errno;
        }
    }
    if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
        pcm->prepared = 0;
        pcm->running = 0;
        if (errno == EPIPE) {
                /* we failed to make our window -- try to restart */
            pcm->underruns++;
            continue;
        }
        return oops(pcm, errno, "cannot read stream data");
    }
    return 0;
}

到了这里,关于pcm设备相关代码解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • alsa音频pcm设备之i2c调试

    i2cdetect 列举 I2C bus i2cdetect -l ls /dev/i2c* 列出I2C bus i2c-7 上面连接的所有设备,并得到i2c设备地址 i2cdetect -y 7 发现i2c设备的位置显示为UU或表示设备地址的数值,UU表示设备在driver中被使用. I2cdump i2c设备大量register的值 i2cdump -y 7 0x40 I2cset设置i2c设备某个register的值 i2cset -y 7 0x40 0x0

    2024年02月08日
    浏览(40)
  • vben-admin 页面以及部分代码 常见问题 解析 持续更新····

    介绍 | Vben Admin (vvbin.cn) https://doc.vvbin.cn/guide/introduction.html ----------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------  

    2024年02月04日
    浏览(44)
  • 如何查看声卡、pcm设备以及tinyplay、tinymix、tinycap的使用 Android tinyAlsa命令详解:

    1、查看当前的声卡: cat /proc/asound/cards 2、查看pcm设备列表: cat /proc/asound/pcm 3、查看当前有哪些进程占用了pcm设备节点 lsof |grep pcm     4、查看有哪些音频设备节点 ls /dev/snd/ 音频设备的命名规则为 [device type] C [card index] D [device index][capture/playback] ,即名字中含有4部分的信息

    2024年02月02日
    浏览(51)
  • PCM音频格式解析和帧大小计算

    目录 一. 什么是PCM? 二、PCM数据格式 三、PCM帧大小计算 PCM(Pulse Code Modulation,脉冲编码调制)音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准数字音频数据。 描述PCM数据的6个参数: Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、

    2023年04月08日
    浏览(35)
  • RISC Zero 的 cargo-risczero相关模块代码解析

    前序博客有: RISC Zero zk-STARK证明系统代码解析 RISC Zero各功能模块代码解析 cargo-risczero模块开源代码见: https://github.com/risc0/risc0/tree/main/risc0/cargo-risczero(Rust) cargo-risczero模块: 用于帮助创建、管理和测试RISC Zero项目的Cargo extension。 RISC Zero toolchain,用于将guest程序,编译为供

    2024年01月19日
    浏览(34)
  • 【通信原理实验】基于A律13折线的PCM编码与解码(附完整代码)

    一、实验原理 PCM,Pulse Code Modulation(脉冲编码调制),即把从模拟信号 抽样、量化、编码 成为二迚制符号的基本过程, 称为脉冲编码调制。 模拟信号的数字化过程: 1、抽样 – 时间离散 :时间连续的信号 - 时间离散、幅度连续的信号; • 抽样定理(香农采样定律、奈奎

    2024年02月08日
    浏览(53)
  • 豆瓣9.7,这部Java神作第3版重磅上市!

    Java 程序员们开年就有重磅好消息,《Effective Java 中文版(原书第 3 版)》要上市啦! 该书的第1版出版于 2001 年,当时就在业界流传开来,受到广泛赞誉。时至今日,已热销近20年,本书第 3 版已是 Java 程序员的必读神书,被誉为“Java 四大名著之一”,甚至连 Java 之父高司

    2024年04月12日
    浏览(31)
  • icloud照片要是关闭有什么影响?

    关闭iCloud照片将对您的设备和照片库产生以下影响: 1. 照片同步停止:关闭iCloud照片后,您的设备将不再自动同步照片到iCloud。这意味着您拍摄的新照片和录制的新视频将不会自动上传到iCloud照片库。                                     2. 存储空间释放:关闭iCloud照片

    2024年01月17日
    浏览(44)
  • redis中的string相关的部分命令

    redis命令手册 redis中文官网查看文档 挨个进行输出调试 Redis Setnx 命令 Redis Getrange 命令 Redis Mset 命令 Redis Setex 命令 Redis SET 命令 Redis Get 命令 Redis Getbit 命令 Redis Setbit 命令 Redis Decr 命令 Redis Decrby 命令 Redis Strlen 命令 Redis Msetnx 命令 Redis Incrby 命令 Redis Incrbyfloat 命令 Redis Setran

    2024年02月02日
    浏览(27)
  • EasyCVR播放设备录像出现部分视频不能播放的原因排查与解决

    EasyCVR视频融合平台基于云边端协同架构,具有强大的数据接入、处理及分发能力。平台支持多协议接入,包括:国标GB28181、RTMP、RTSP/Onvif、海康Ehome、海康SDK、大华SDK、宇视SDK等,对外可分发多格式视频流,包括RTSP、RTMP、FLV、HLS、WebRTC等。 有用户反馈,通过SDK接入的设备,

    2024年02月12日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包