首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ALSA:播放音频阻塞模式和线程的正确方法

ALSA:播放音频阻塞模式和线程的正确方法
EN

Stack Overflow用户
提问于 2018-04-30 23:42:01
回答 2查看 2.7K关注 0票数 1

我无法正确地编写循环来播放音频和/或我不能正确理解Alsa的start_threshold、stop_threshold和avail_min参数的含义。我希望达到最低的延迟,并知道要修改的正确参数,以便使我的捕获-处理-回放链能够容忍音频处理时间的变化。在这个例子中,我从文件中读取样本,重点放在回放部分。

这是我的输出设备配置,在阻塞模式下打开(Ubuntu16.04,PulseAudio present,default设备):

代码语言:javascript
运行
复制
ALSA <-> PulseAudio PCM I/O Plugin
Its setup is:
  stream       : PLAYBACK
  access       : RW_INTERLEAVED
  format       : FLOAT_LE
  subformat    : STD
  channels     : 1
  rate         : 44100
  exact rate   : 44100 (44100/1)
  msbits       : 32
  buffer_size  : 6144
  period_size  : 2048
  period_time  : 46439
  tstamp_mode  : NONE
  tstamp_type  : GETTIMEOFDAY
  period_step  : 1
  avail_min    : 4096
  period_event : 0
  start_threshold  : 2048
  stop_threshold   : 2048
  silence_threshold: 0
  silence_size : 0
  boundary     : 6917529027641081856

我的回放线程从另一个线程获取音频缓冲区,该线程从文件中读取样本(因此比回放速率更快)。这是播放函数(Cython代码):

代码语言:javascript
运行
复制
cdef int play_buffer(self, buffer_t *buf) nogil:

    cdef int rc
    cdef long t0, t1

    t0 = timestamp_us()
    rc = snd_pcm_writei(self.handle, buf.data, buf.period_size)
    t1 = timestamp_us()

    if rc >= 0:
        printf("[%ld / %6ld] snd_pcm_writei: OK %d\n", t0, t1 - t0, rc)
    else:
        printf("[%ld / %6ld] snd_pcm_writei: ERR %d [%s]\n", t0, t1 - t0, rc, snd_strerror(rc))

    if rc < 0:
        if rc == -errno.EPIPE:
            rc = snd_pcm_prepare(self.handle)
            printf("snd_pcm_prepare: %d\n", rc)

        else:
            printf("play_buffer ERR %d\n", rc)
            return WRITE_ERROR

    elif rc != buf.period_size:
        printf('snd_pcm_writei(): short write %d != %d', rc, buf.period_size)

    return OK

这是输出(timestamp_us()以微秒为单位返回系统时间):

代码语言:javascript
运行
复制
[1525102519583090 /    540] snd_pcm_writei: OK 2048
[1525102519585406 /     16] snd_pcm_writei: ERR -32 [Broken pipe]
snd_pcm_prepare: 0
[1525102519587798 /    393] snd_pcm_writei: OK 2048
[1525102519590018 /      3] snd_pcm_writei: ERR -32 [Broken pipe]
snd_pcm_prepare: 0
[1525102519592303 /    415] snd_pcm_writei: OK 2048
[1525102519594523 /      3] snd_pcm_writei: ERR -32 [Broken pipe]
snd_pcm_prepare: 0
[1525102519596823 /   1905] snd_pcm_writei: OK 2048
[1525102519599242 /     12] snd_pcm_writei: ERR -32 [Broken pipe]
snd_pcm_prepare: 0
[1525102519601707 /   8023] snd_pcm_writei: OK 2048
[1525102519609754 /     45] snd_pcm_writei: OK 2048
[1525102519609811 /     27] snd_pcm_writei: OK 2048
[1525102519609847 /     28] snd_pcm_writei: OK 2048
[1525102519611328 /     40] snd_pcm_writei: OK 2048
[1525102519613546 /  48501] snd_pcm_writei: OK 2048
[1525102519662079 /  50678] snd_pcm_writei: OK 2048
[1525102519712804 /  49487] snd_pcm_writei: OK 2048
[1525102519762318 /  50521] snd_pcm_writei: OK 2048
[1525102519812868 /  49497] snd_pcm_writei: OK 2048
[1525102519862394 /  49630] snd_pcm_writei: OK 2048
[1525102519912051 /  49875] snd_pcm_writei: OK 2048
[1525102519961953 /  50647] snd_pcm_writei: OK 2048
[1525102520012655 /  49949] snd_pcm_writei: OK 2048
[1525102520062632 /  49713] snd_pcm_writei: OK 2048
[1525102520112373 /  49495] snd_pcm_writei: OK 2048
[1525102520161898 /     62] snd_pcm_writei: OK 2048
[1525102520161977 /  50485] snd_pcm_writei: OK 2048
[1525102520212490 /  49514] snd_pcm_writei: OK 2048
.. continues with no errors ...

一开始我不知道这些EPIPE错误的原因;我期望的是snd_pcm_writei要么立即返回,要么(或多或少)在返回之前等待一段时间,因为我处于阻塞模式,并且提供样本的速度比回放采样率所需的速度更快。当错误序列结束时,播放是正常的。此外,如果我为我的线程设置实时优先级(pthread_setschedparam()),我会得到一个无穷无尽的OK 2048 / ERR -32列表,并且我只听到噪音。这对我来说真的很奇怪。

我的错误在哪里?

谢谢。

EN

Stack Overflow用户

发布于 2018-05-01 00:33:28

一开始,缓冲区是空的,所以你应该尽可能快地填满它,而不是等待。

要减少延迟,请减小缓冲区大小。

要降低欠载运行的风险,请增加缓冲区大小。

你不能同时做这两件事。您必须为您的特定情况选择一个平衡这两个目标的缓冲区大小。

开始阈值指定当缓冲区中有那么多帧时,设备自动开始播放。您应该将其设置为缓冲区大小。

停止阈值指定当可用(空闲)帧数达到此值时设备停止。您应该将其保留为默认值,即缓冲区大小。

avail_min参数指定在中断导致应用程序实际被唤醒之前必须有多少帧可用。

票数 2
EN
查看全部 2 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50103858

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档