在没有 snd_pcm_pause() 的情况下暂停 ALSA pcm 句柄

问题描述 投票:0回答:1

我正在开发一个用 C 语言编写的音频播放器,它使用 libasound (ALSA) 作为音频后端。我实现了一个回调机制,允许音频播放器及时向 ALSA 发送音频。我将 pcm_handle 配置为在内部保存一个包含 500ms (= buffer_time) 音频数据的缓冲区。通过在 ALSA 的轮询描述符上使用

poll()
,程序会收到通知向缓冲区添加更多数据。我还
poll()
在我自己的自定义轮询描述符上,以便通知循环何时停止/暂停。

我希望能够暂停音频输出(就像暂停歌曲一样),因此我必须暂停 pcm 句柄,即告诉 ALSA 停止从内部缓冲区向声卡发送音频帧。人们可以使用

snd_pcm_pause()
函数,但是,正如 文档 所示,该函数并不适用于所有硬件。我的音频播放器是针对嵌入式设备的,所以我想支持所有硬件,因此我不想使用该功能。

或者,我可以使用

snd_pcm_drain()
函数,该函数将在 500ms 缓冲区中的所有待处理音频样本播放完毕后暂停 pcm 句柄。然而,这将导致高达 500 毫秒的延迟。通过减小缓冲区大小可以最大限度地减少这种延迟,但这永远不会消除问题,并且会增加欠载运行的机会。

另一种选择是使用

snd_pcm_drop()
函数,该函数将暂停 pcm 句柄并丢弃缓冲区中任何待处理的音频样本。这解决了延迟问题,但结果是当 pcm_handle 重新启动时,一些音频样本会丢失,这使得该选项也不理想。

我个人正在考虑使用

snd_pcm_drop()
功能。为了解决丢失样本的问题,我正在寻找一种方法来检索 ALSA 缓冲区中所有待处理的样本,以便我可以在 pcm 句柄再次启动时立即播放它们。我在调用
snd_pcm_readi()
之前尝试使用
snd_pcm_drop()
来检索待处理的样本,但这给了我分段错误。我相信 ALSA 不允许在
SND_PCM_STREAM_PLAYBACK
句柄上使用此功能。

那么是否有另一种选择可以在没有延迟且不会丢失挂起的音频帧的情况下暂停 pcm 句柄?如果没有,按照我的解决方案中的建议,有没有办法从 ALSA 的内部缓冲区检索那些待处理的帧?

我的实现与此处所示的“写入并等待传输方法”非常相似。以下伪代码给出了我的实现的简化版本(没有当前问题的解决方案):

write_and_poll_loop(...) {
    while(1) {
        poll(...) ;    /** Here we wait for room in the buffer or for a pause command to come in */
        if(ALSA_buffer_has_room) {
            customAudioCallback();        /** In this callback audio samples are written to the ALSA buffer */
        }
        else if (pause_command) {
            snd_pcm_drop();               /** Discard all samples in the ALSA buffer */
            snd_pcm_perpare();            /** Prepare pcm_handle for future playback */
            block_until_play_command()    /** Block until we want to play audio again */
        }    
    }

}

编辑:我意识到通过使用

snd_pcm_hw_params_can_pause()
我可以检查硬件是否支持暂停,如果不支持,我就退回到
snd_pcm_drop()
方法,只需付出丢失样本的代价。尽管如此,我希望看到一个独立于硬件的解决方案。

c audio alsa pause libasound
1个回答
0
投票

我怀疑它不是独立于硬件的(即某些硬件可能不支持它,所以您可能还需要提到的后备),但我知道还有一个选项;在调用

snd_pcm_drain()
之前,您可以使用
snd_pcm_rewind()
删除待处理的帧。一般来说,倒带可以让您使用大周期缓冲区,同时仍然能够快速做出反应并覆盖已调度的帧,但代价是增加了实现复杂性。

否则,虽然它可能不准确,但我认为使用

snd_pcm_status_get_avail()
(或者与
snd_pcm_status_get_htstamp
或其他时间戳选项一起更复杂,我还没有看过所有这些)你应该能够在调用
snd_pcm_drop()
的时间点相当准确地估计当前播放位置,至少足以能够在没有任何声音跳过的情况下恢复。

© www.soinside.com 2019 - 2024. All rights reserved.