Docker:在多个容器上共享/ dev / snd会导致“设备或资源繁忙”

nor*_*bjd 6 alsa docker

添加主机设备--device /dev/snd)到多克尔容器,我有时会遇到Device or resource busy错误。

我用一个涉及音频(alsa)的最小示例重现了该问题。这是我的Dockerfile(产生图像docker-device-example):

FROM    debian:buster

RUN     apt-get update \
 &&     apt-get install -y --no-install-recommends \
            alsa-utils \
 &&     rm -rf /var/lib/apt/lists/*
Run Code Online (Sandbox Code Playgroud)

我正在speaker-test使用/dev/snd共享运行以下命令(该工具是生成可用来测试扬声器的音调的工具):

docker run --rm \
    -i -t \
    --device /dev/snd \
    docker-device-example \
    speaker-test
Run Code Online (Sandbox Code Playgroud)

问题

当运行上一个命令时,会发出粉红声音,但仅在某些情况下

  • 如果我没有在主机上播放任何声音:例如,如果我正在播放视频,并且即使视频已暂停,命令也会失败
  • 如果我没有运行另一个容器来访问/dev/snd设备

使用时看起来像是/dev/snd“锁定”的,如果是这种情况,我得到以下输出(错误由后两行表示):

speaker-test 1.1.6

Playback device is default
Stream parameters are 48000Hz, S16_LE, 1 channels
Using 16 octaves of pink noise
ALSA lib pcm_dmix.c:1099:(snd_pcm_dmix_open) unable to open slave
Playback open error: -16,Device or resource busy
Run Code Online (Sandbox Code Playgroud)

而且,反之亦然,如果在容器上播放了粉红色的声音,则我无法在主机上播放任何声音(Ubuntu)。但是主机上的命令不会因相同的消息而失败。而是aplay test.wav无限期地阻止主机上的命令(例如播放简单的声音)(即使随后关闭容器)。

我试图通过运行进行调试strace aplay test.way,并且该命令似乎在poll系统调用中被阻止:

poll([{fd=3, events=POLLIN|POLLERR|POLLNVAL}], 1, 4294967295
Run Code Online (Sandbox Code Playgroud)

如何从两个(或更多)不同的容器中播放声音,或者从主机和容器中同时播放声音?

附加信息

我已经用再现了这个问题/dev/snd,但是我不知道在使用其他设备时是否发生了类似的事情,或者它是否与声音设备或者有关alsa

还要注意,当同时运行多个speaker-testaplay命令并且全部在我的主机上运行时(不涉及容器),则将播放所有声音。

mvi*_*eck 5

我不知道如何用 ALSA 解决这个问题,但可以提供 2 种可能的脉冲音频方法。如果这些设置失败,请安装pulseaudio在映像中以确保依赖项已满。

ALSA 直接访问声音硬件并阻止其他客户端访问它。但是可以设置 ALSA 来为多个客户端提供服务。这必须由其他人来回答。可能一些ALSA dmix 插件设置是要走的路。


  1. 带有共享套接字的 Pulseaudio:

创建pulseaudio套接字:

pactl load-module module-native-protocol-unix socket=/tmp/pulseaudio.socket
Run Code Online (Sandbox Code Playgroud)

/tmp/pulseaudio.client.conf为pulseaudio客户端创建:

default-server = unix:/tmp/pulseaudio.socket
# Prevent a server running in the container
autospawn = no
daemon-binary = /bin/true
# Prevent the use of shared memory
enable-shm = false
Run Code Online (Sandbox Code Playgroud)

与 docker 共享套接字和配置文件并设置环境变量PULSE_SERVERPULSE_COOKIE. 容器用户必须与主机相同:

docker run --rm \
    --env PULSE_SERVER=unix:/tmp/pulseaudio.socket \
    --env PULSE_COOKIE=/tmp/pulseaudio.cookie \
    --volume /tmp/pulseaudio.socket:/tmp/pulseaudio.socket \
    --volume /tmp/pulseaudio.client.conf:/etc/pulse/client.conf \
    --user $(id -u):$(id -g) \
    imagename
Run Code Online (Sandbox Code Playgroud)

cookie 将由pulseaudio 本身创建。


  1. TCP 上的 Pulseaudio:

从主机获取IP地址:

# either an arbitrary IPv4 address
Hostip="$(ip -4 -o a | awk '{print $4}' | cut -d/ -f1 | grep -v 127.0.0.1 | head -n1)"

# or especially IP from docker daemon
Hostip="$(ip -4 -o a| grep docker0 | awk '{print $4}' | cut -d/ -f1)"
Run Code Online (Sandbox Code Playgroud)

运行 docker 镜像。你需要一个空闲的 TCP 端口,这里34567使用。(TCP 端口号必须在 范围内cat /proc/sys/net/ipv4/ip_local_port_range且不得在使用中。请检查ss -nlp | grep 34567。)

docker run --rm \
    --name pulsecontainer \
    --env PULSE_SERVER=tcp:$Hostip:34567 \
    imagename
Run Code Online (Sandbox Code Playgroud)

使用以下命令docker run获取容器的 IP 后:

Containerip="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' pulsecontainer)"
Run Code Online (Sandbox Code Playgroud)

加载经过容器 IP 认证的pulseaudio TCP 模块:

pactl load-module module-native-protocol-tcp  port=34567 auth-ip-acl=$Containerip
Run Code Online (Sandbox Code Playgroud)

请注意,TCP 模块是容器启动并运行加载的。脉冲音频服务器可用于容器应用程序需要一些时间。如果 TCP 连接失败,请检查iptablesufw设置。


总结这些设置的方法:https : //github.com/mviereck/x11docker/wiki/Container-sound : -ALSA -or-Pulseaudio